ce62844112c7360d8d833ad87ba72c7ae53325f7
[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
1173 /* ------------------------------------------------------------------------- */
1174 /* definition of elements that automatically change to other elements after  */
1175 /* a specified time, eventually calling a function when changing             */
1176 /* ------------------------------------------------------------------------- */
1177
1178 /* forward declaration for changer functions */
1179 static void InitBuggyBase(int, int);
1180 static void WarnBuggyBase(int, int);
1181
1182 static void InitTrap(int, int);
1183 static void ActivateTrap(int, int);
1184 static void ChangeActiveTrap(int, int);
1185
1186 static void InitRobotWheel(int, int);
1187 static void RunRobotWheel(int, int);
1188 static void StopRobotWheel(int, int);
1189
1190 static void InitTimegateWheel(int, int);
1191 static void RunTimegateWheel(int, int);
1192
1193 static void InitMagicBallDelay(int, int);
1194 static void ActivateMagicBall(int, int);
1195
1196 struct ChangingElementInfo
1197 {
1198   int element;
1199   int target_element;
1200   int change_delay;
1201   void (*pre_change_function)(int x, int y);
1202   void (*change_function)(int x, int y);
1203   void (*post_change_function)(int x, int y);
1204 };
1205
1206 static struct ChangingElementInfo change_delay_list[] =
1207 {
1208   {
1209     EL_NUT_BREAKING,
1210     EL_EMERALD,
1211     6,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_PEARL_BREAKING,
1218     EL_EMPTY,
1219     8,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EXIT_OPENING,
1226     EL_EXIT_OPEN,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_CLOSING,
1234     EL_EXIT_CLOSED,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_STEEL_EXIT_OPENING,
1242     EL_STEEL_EXIT_OPEN,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_CLOSING,
1250     EL_STEEL_EXIT_CLOSED,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_EM_EXIT_OPENING,
1258     EL_EM_EXIT_OPEN,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_CLOSING,
1266 #if 1
1267     EL_EMPTY,
1268 #else
1269     EL_EM_EXIT_CLOSED,
1270 #endif
1271     29,
1272     NULL,
1273     NULL,
1274     NULL
1275   },
1276   {
1277     EL_EM_STEEL_EXIT_OPENING,
1278     EL_EM_STEEL_EXIT_OPEN,
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_CLOSING,
1286 #if 1
1287     EL_STEELWALL,
1288 #else
1289     EL_EM_STEEL_EXIT_CLOSED,
1290 #endif
1291     29,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_EXIT_OPENING,
1298     EL_SP_EXIT_OPEN,
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_CLOSING,
1306     EL_SP_EXIT_CLOSED,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SWITCHGATE_OPENING,
1314     EL_SWITCHGATE_OPEN,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_CLOSING,
1322     EL_SWITCHGATE_CLOSED,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_TIMEGATE_OPENING,
1330     EL_TIMEGATE_OPEN,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_CLOSING,
1338     EL_TIMEGATE_CLOSED,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344
1345   {
1346     EL_ACID_SPLASH_LEFT,
1347     EL_EMPTY,
1348     8,
1349     NULL,
1350     NULL,
1351     NULL
1352   },
1353   {
1354     EL_ACID_SPLASH_RIGHT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_SP_BUGGY_BASE,
1363     EL_SP_BUGGY_BASE_ACTIVATING,
1364     0,
1365     InitBuggyBase,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     EL_SP_BUGGY_BASE_ACTIVE,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     EL_SP_BUGGY_BASE,
1380     0,
1381     InitBuggyBase,
1382     WarnBuggyBase,
1383     NULL
1384   },
1385   {
1386     EL_TRAP,
1387     EL_TRAP_ACTIVE,
1388     0,
1389     InitTrap,
1390     NULL,
1391     ActivateTrap
1392   },
1393   {
1394     EL_TRAP_ACTIVE,
1395     EL_TRAP,
1396     31,
1397     NULL,
1398     ChangeActiveTrap,
1399     NULL
1400   },
1401   {
1402     EL_ROBOT_WHEEL_ACTIVE,
1403     EL_ROBOT_WHEEL,
1404     0,
1405     InitRobotWheel,
1406     RunRobotWheel,
1407     StopRobotWheel
1408   },
1409   {
1410     EL_TIMEGATE_SWITCH_ACTIVE,
1411     EL_TIMEGATE_SWITCH,
1412     0,
1413     InitTimegateWheel,
1414     RunTimegateWheel,
1415     NULL
1416   },
1417   {
1418     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1419     EL_DC_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_EMC_MAGIC_BALL_ACTIVE,
1427     EL_EMC_MAGIC_BALL_ACTIVE,
1428     0,
1429     InitMagicBallDelay,
1430     NULL,
1431     ActivateMagicBall
1432   },
1433   {
1434     EL_EMC_SPRING_BUMPER_ACTIVE,
1435     EL_EMC_SPRING_BUMPER,
1436     8,
1437     NULL,
1438     NULL,
1439     NULL
1440   },
1441   {
1442     EL_DIAGONAL_SHRINKING,
1443     EL_UNDEFINED,
1444     0,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_GROWING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL,
1456   },
1457
1458   {
1459     EL_UNDEFINED,
1460     EL_UNDEFINED,
1461     -1,
1462     NULL,
1463     NULL,
1464     NULL
1465   }
1466 };
1467
1468 struct
1469 {
1470   int element;
1471   int push_delay_fixed, push_delay_random;
1472 }
1473 push_delay_list[] =
1474 {
1475   { EL_SPRING,                  0, 0 },
1476   { EL_BALLOON,                 0, 0 },
1477
1478   { EL_SOKOBAN_OBJECT,          2, 0 },
1479   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1480   { EL_SATELLITE,               2, 0 },
1481   { EL_SP_DISK_YELLOW,          2, 0 },
1482
1483   { EL_UNDEFINED,               0, 0 },
1484 };
1485
1486 struct
1487 {
1488   int element;
1489   int move_stepsize;
1490 }
1491 move_stepsize_list[] =
1492 {
1493   { EL_AMOEBA_DROP,             2 },
1494   { EL_AMOEBA_DROPPING,         2 },
1495   { EL_QUICKSAND_FILLING,       1 },
1496   { EL_QUICKSAND_EMPTYING,      1 },
1497   { EL_QUICKSAND_FAST_FILLING,  2 },
1498   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1499   { EL_MAGIC_WALL_FILLING,      2 },
1500   { EL_MAGIC_WALL_EMPTYING,     2 },
1501   { EL_BD_MAGIC_WALL_FILLING,   2 },
1502   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1503   { EL_DC_MAGIC_WALL_FILLING,   2 },
1504   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1505
1506   { EL_UNDEFINED,               0 },
1507 };
1508
1509 struct
1510 {
1511   int element;
1512   int count;
1513 }
1514 collect_count_list[] =
1515 {
1516   { EL_EMERALD,                 1 },
1517   { EL_BD_DIAMOND,              1 },
1518   { EL_EMERALD_YELLOW,          1 },
1519   { EL_EMERALD_RED,             1 },
1520   { EL_EMERALD_PURPLE,          1 },
1521   { EL_DIAMOND,                 3 },
1522   { EL_SP_INFOTRON,             1 },
1523   { EL_PEARL,                   5 },
1524   { EL_CRYSTAL,                 8 },
1525
1526   { EL_UNDEFINED,               0 },
1527 };
1528
1529 struct
1530 {
1531   int element;
1532   int direction;
1533 }
1534 access_direction_list[] =
1535 {
1536   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1537   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1538   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1539   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1540   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1542   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1543   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1544   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1545   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1546   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1547
1548   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1549   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1550   { EL_SP_PORT_UP,                                                   MV_DOWN },
1551   { EL_SP_PORT_DOWN,                                         MV_UP           },
1552   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1553   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1554   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1555   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1556   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1557   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1558   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1559   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1560   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1561   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1562   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1563   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1564   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1565   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1566   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1567
1568   { EL_UNDEFINED,                       MV_NONE                              }
1569 };
1570
1571 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1572
1573 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1574 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1575 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1576                                  IS_JUST_CHANGING(x, y))
1577
1578 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1579
1580 /* static variables for playfield scan mode (scanning forward or backward) */
1581 static int playfield_scan_start_x = 0;
1582 static int playfield_scan_start_y = 0;
1583 static int playfield_scan_delta_x = 1;
1584 static int playfield_scan_delta_y = 1;
1585
1586 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1587                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1588                                      (y) += playfield_scan_delta_y)     \
1589                                 for ((x) = playfield_scan_start_x;      \
1590                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1591                                      (x) += playfield_scan_delta_x)
1592
1593 #ifdef DEBUG
1594 void DEBUG_SetMaximumDynamite()
1595 {
1596   int i;
1597
1598   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1599     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1600       local_player->inventory_element[local_player->inventory_size++] =
1601         EL_DYNAMITE;
1602 }
1603 #endif
1604
1605 static void InitPlayfieldScanModeVars()
1606 {
1607   if (game.use_reverse_scan_direction)
1608   {
1609     playfield_scan_start_x = lev_fieldx - 1;
1610     playfield_scan_start_y = lev_fieldy - 1;
1611
1612     playfield_scan_delta_x = -1;
1613     playfield_scan_delta_y = -1;
1614   }
1615   else
1616   {
1617     playfield_scan_start_x = 0;
1618     playfield_scan_start_y = 0;
1619
1620     playfield_scan_delta_x = 1;
1621     playfield_scan_delta_y = 1;
1622   }
1623 }
1624
1625 static void InitPlayfieldScanMode(int mode)
1626 {
1627   game.use_reverse_scan_direction =
1628     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1629
1630   InitPlayfieldScanModeVars();
1631 }
1632
1633 static int get_move_delay_from_stepsize(int move_stepsize)
1634 {
1635   move_stepsize =
1636     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1637
1638   /* make sure that stepsize value is always a power of 2 */
1639   move_stepsize = (1 << log_2(move_stepsize));
1640
1641   return TILEX / move_stepsize;
1642 }
1643
1644 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1645                                boolean init_game)
1646 {
1647   int player_nr = player->index_nr;
1648   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1649   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1650
1651   /* do no immediately change move delay -- the player might just be moving */
1652   player->move_delay_value_next = move_delay;
1653
1654   /* information if player can move must be set separately */
1655   player->cannot_move = cannot_move;
1656
1657   if (init_game)
1658   {
1659     player->move_delay       = game.initial_move_delay[player_nr];
1660     player->move_delay_value = game.initial_move_delay_value[player_nr];
1661
1662     player->move_delay_value_next = -1;
1663
1664     player->move_delay_reset_counter = 0;
1665   }
1666 }
1667
1668 void GetPlayerConfig()
1669 {
1670   GameFrameDelay = setup.game_frame_delay;
1671
1672   if (!audio.sound_available)
1673     setup.sound_simple = FALSE;
1674
1675   if (!audio.loops_available)
1676     setup.sound_loops = FALSE;
1677
1678   if (!audio.music_available)
1679     setup.sound_music = FALSE;
1680
1681   if (!video.fullscreen_available)
1682     setup.fullscreen = FALSE;
1683
1684   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1685
1686   SetAudioMode(setup.sound);
1687   InitJoysticks();
1688 }
1689
1690 int GetElementFromGroupElement(int element)
1691 {
1692   if (IS_GROUP_ELEMENT(element))
1693   {
1694     struct ElementGroupInfo *group = element_info[element].group;
1695     int last_anim_random_frame = gfx.anim_random_frame;
1696     int element_pos;
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = RND(group->num_elements_resolved);
1700
1701     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1702                                     group->choice_mode, 0,
1703                                     group->choice_pos);
1704
1705     if (group->choice_mode == ANIM_RANDOM)
1706       gfx.anim_random_frame = last_anim_random_frame;
1707
1708     group->choice_pos++;
1709
1710     element = group->element_resolved[element_pos];
1711   }
1712
1713   return element;
1714 }
1715
1716 static void InitPlayerField(int x, int y, int element, boolean init_game)
1717 {
1718   if (element == EL_SP_MURPHY)
1719   {
1720     if (init_game)
1721     {
1722       if (stored_player[0].present)
1723       {
1724         Feld[x][y] = EL_SP_MURPHY_CLONE;
1725
1726         return;
1727       }
1728       else
1729       {
1730         stored_player[0].initial_element = element;
1731         stored_player[0].use_murphy = TRUE;
1732
1733         if (!level.use_artwork_element[0])
1734           stored_player[0].artwork_element = EL_SP_MURPHY;
1735       }
1736
1737       Feld[x][y] = EL_PLAYER_1;
1738     }
1739   }
1740
1741   if (init_game)
1742   {
1743     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1744     int jx = player->jx, jy = player->jy;
1745
1746     player->present = TRUE;
1747
1748     player->block_last_field = (element == EL_SP_MURPHY ?
1749                                 level.sp_block_last_field :
1750                                 level.block_last_field);
1751
1752     /* ---------- initialize player's last field block delay --------------- */
1753
1754     /* always start with reliable default value (no adjustment needed) */
1755     player->block_delay_adjustment = 0;
1756
1757     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1758     if (player->block_last_field && element == EL_SP_MURPHY)
1759       player->block_delay_adjustment = 1;
1760
1761     /* special case 2: in game engines before 3.1.1, blocking was different */
1762     if (game.use_block_last_field_bug)
1763       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1764
1765     if (!options.network || player->connected)
1766     {
1767       player->active = TRUE;
1768
1769       /* remove potentially duplicate players */
1770       if (StorePlayer[jx][jy] == Feld[x][y])
1771         StorePlayer[jx][jy] = 0;
1772
1773       StorePlayer[x][y] = Feld[x][y];
1774
1775       if (options.debug)
1776       {
1777         printf("Player %d activated.\n", player->element_nr);
1778         printf("[Local player is %d and currently %s.]\n",
1779                local_player->element_nr,
1780                local_player->active ? "active" : "not active");
1781       }
1782     }
1783
1784     Feld[x][y] = EL_EMPTY;
1785
1786     player->jx = player->last_jx = x;
1787     player->jy = player->last_jy = y;
1788   }
1789
1790 #if USE_PLAYER_REANIMATION
1791   if (!init_game)
1792   {
1793     int player_nr = GET_PLAYER_NR(element);
1794     struct PlayerInfo *player = &stored_player[player_nr];
1795
1796     if (player->active && player->killed)
1797       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1798   }
1799 #endif
1800 }
1801
1802 static void InitField(int x, int y, boolean init_game)
1803 {
1804   int element = Feld[x][y];
1805
1806   switch (element)
1807   {
1808     case EL_SP_MURPHY:
1809     case EL_PLAYER_1:
1810     case EL_PLAYER_2:
1811     case EL_PLAYER_3:
1812     case EL_PLAYER_4:
1813       InitPlayerField(x, y, element, init_game);
1814       break;
1815
1816     case EL_SOKOBAN_FIELD_PLAYER:
1817       element = Feld[x][y] = EL_PLAYER_1;
1818       InitField(x, y, init_game);
1819
1820       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1821       InitField(x, y, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_EMPTY:
1825       local_player->sokobanfields_still_needed++;
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       local_player->lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       local_player->friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    /* more than one switch -- set it like the first switch */
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952 #if !USE_BOTH_SWITCHGATE_SWITCHES
1953     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1954       if (init_game)
1955         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1956       break;
1957
1958     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1959       if (init_game)
1960         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1961       break;
1962 #endif
1963
1964     case EL_LIGHT_SWITCH_ACTIVE:
1965       if (init_game)
1966         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1967       break;
1968
1969     case EL_INVISIBLE_STEELWALL:
1970     case EL_INVISIBLE_WALL:
1971     case EL_INVISIBLE_SAND:
1972       if (game.light_time_left > 0 ||
1973           game.lenses_time_left > 0)
1974         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1975       break;
1976
1977     case EL_EMC_MAGIC_BALL:
1978       if (game.ball_state)
1979         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1980       break;
1981
1982     case EL_EMC_MAGIC_BALL_SWITCH:
1983       if (game.ball_state)
1984         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1985       break;
1986
1987     case EL_TRIGGER_PLAYER:
1988     case EL_TRIGGER_ELEMENT:
1989     case EL_TRIGGER_CE_VALUE:
1990     case EL_TRIGGER_CE_SCORE:
1991     case EL_SELF:
1992     case EL_ANY_ELEMENT:
1993     case EL_CURRENT_CE_VALUE:
1994     case EL_CURRENT_CE_SCORE:
1995     case EL_PREV_CE_1:
1996     case EL_PREV_CE_2:
1997     case EL_PREV_CE_3:
1998     case EL_PREV_CE_4:
1999     case EL_PREV_CE_5:
2000     case EL_PREV_CE_6:
2001     case EL_PREV_CE_7:
2002     case EL_PREV_CE_8:
2003     case EL_NEXT_CE_1:
2004     case EL_NEXT_CE_2:
2005     case EL_NEXT_CE_3:
2006     case EL_NEXT_CE_4:
2007     case EL_NEXT_CE_5:
2008     case EL_NEXT_CE_6:
2009     case EL_NEXT_CE_7:
2010     case EL_NEXT_CE_8:
2011       /* reference elements should not be used on the playfield */
2012       Feld[x][y] = EL_EMPTY;
2013       break;
2014
2015     default:
2016       if (IS_CUSTOM_ELEMENT(element))
2017       {
2018         if (CAN_MOVE(element))
2019           InitMovDir(x, y);
2020
2021 #if USE_NEW_CUSTOM_VALUE
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2024 #endif
2025       }
2026       else if (IS_GROUP_ELEMENT(element))
2027       {
2028         Feld[x][y] = GetElementFromGroupElement(element);
2029
2030         InitField(x, y, init_game);
2031       }
2032
2033       break;
2034   }
2035
2036   if (!init_game)
2037     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2038 }
2039
2040 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2041 {
2042   InitField(x, y, init_game);
2043
2044   /* not needed to call InitMovDir() -- already done by InitField()! */
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(Feld[x][y]))
2047     InitMovDir(x, y);
2048 }
2049
2050 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2051 {
2052   int old_element = Feld[x][y];
2053
2054   InitField(x, y, init_game);
2055
2056   /* not needed to call InitMovDir() -- already done by InitField()! */
2057   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2058       CAN_MOVE(old_element) &&
2059       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2060     InitMovDir(x, y);
2061
2062   /* this case is in fact a combination of not less than three bugs:
2063      first, it calls InitMovDir() for elements that can move, although this is
2064      already done by InitField(); then, it checks the element that was at this
2065      field _before_ the call to InitField() (which can change it); lastly, it
2066      was not called for "mole with direction" elements, which were treated as
2067      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2068   */
2069 }
2070
2071 #if 1
2072
2073 static int get_key_element_from_nr(int key_nr)
2074 {
2075   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2076                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2077                           EL_EM_KEY_1 : EL_KEY_1);
2078
2079   return key_base_element + key_nr;
2080 }
2081
2082 static int get_next_dropped_element(struct PlayerInfo *player)
2083 {
2084   return (player->inventory_size > 0 ?
2085           player->inventory_element[player->inventory_size - 1] :
2086           player->inventory_infinite_element != EL_UNDEFINED ?
2087           player->inventory_infinite_element :
2088           player->dynabombs_left > 0 ?
2089           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2090           EL_UNDEFINED);
2091 }
2092
2093 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2094 {
2095   /* pos >= 0: get element from bottom of the stack;
2096      pos <  0: get element from top of the stack */
2097
2098   if (pos < 0)
2099   {
2100     int min_inventory_size = -pos;
2101     int inventory_pos = player->inventory_size - min_inventory_size;
2102     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2103
2104     return (player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             player->inventory_infinite_element != EL_UNDEFINED ?
2107             player->inventory_infinite_element :
2108             player->dynabombs_left >= min_dynabombs_left ?
2109             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2110             EL_UNDEFINED);
2111   }
2112   else
2113   {
2114     int min_dynabombs_left = pos + 1;
2115     int min_inventory_size = pos + 1 - player->dynabombs_left;
2116     int inventory_pos = pos - player->dynabombs_left;
2117
2118     return (player->inventory_infinite_element != EL_UNDEFINED ?
2119             player->inventory_infinite_element :
2120             player->dynabombs_left >= min_dynabombs_left ?
2121             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122             player->inventory_size >= min_inventory_size ?
2123             player->inventory_element[inventory_pos] :
2124             EL_UNDEFINED);
2125   }
2126 }
2127
2128 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2129 {
2130   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2131   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2132   int compare_result;
2133
2134   if (gpo1->sort_priority != gpo2->sort_priority)
2135     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2136   else
2137     compare_result = gpo1->nr - gpo2->nr;
2138
2139   return compare_result;
2140 }
2141
2142 void InitGameControlValues()
2143 {
2144   int i;
2145
2146   for (i = 0; game_panel_controls[i].nr != -1; i++)
2147   {
2148     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2149     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2150     struct TextPosInfo *pos = gpc->pos;
2151     int nr = gpc->nr;
2152     int type = gpc->type;
2153
2154     if (nr != i)
2155     {
2156       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2157       Error(ERR_EXIT, "this should not happen -- please debug");
2158     }
2159
2160     /* force update of game controls after initialization */
2161     gpc->value = gpc->last_value = -1;
2162     gpc->frame = gpc->last_frame = -1;
2163     gpc->gfx_frame = -1;
2164
2165     /* determine panel value width for later calculation of alignment */
2166     if (type == TYPE_INTEGER || type == TYPE_STRING)
2167     {
2168       pos->width = pos->size * getFontWidth(pos->font);
2169       pos->height = getFontHeight(pos->font);
2170     }
2171     else if (type == TYPE_ELEMENT)
2172     {
2173       pos->width = pos->size;
2174       pos->height = pos->size;
2175     }
2176
2177     /* fill structure for game panel draw order */
2178     gpo->nr = gpc->nr;
2179     gpo->sort_priority = pos->sort_priority;
2180   }
2181
2182   /* sort game panel controls according to sort_priority and control number */
2183   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2184         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2185 }
2186
2187 void UpdatePlayfieldElementCount()
2188 {
2189   boolean use_element_count = FALSE;
2190   int i, j, x, y;
2191
2192   /* first check if it is needed at all to calculate playfield element count */
2193   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2194     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2195       use_element_count = TRUE;
2196
2197   if (!use_element_count)
2198     return;
2199
2200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2201     element_info[i].element_count = 0;
2202
2203   SCAN_PLAYFIELD(x, y)
2204   {
2205     element_info[Feld[x][y]].element_count++;
2206   }
2207
2208   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2209     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2210       if (IS_IN_GROUP(j, i))
2211         element_info[EL_GROUP_START + i].element_count +=
2212           element_info[j].element_count;
2213 }
2214
2215 void UpdateGameControlValues()
2216 {
2217   int i, k;
2218   int time = (local_player->LevelSolved ?
2219               local_player->LevelSolved_CountingTime :
2220               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2221               level.native_em_level->lev->time :
2222               level.time == 0 ? TimePlayed : TimeLeft);
2223   int score = (local_player->LevelSolved ?
2224                local_player->LevelSolved_CountingScore :
2225                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226                level.native_em_level->lev->score :
2227                local_player->score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               local_player->gems_still_needed);
2231   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232                      level.native_em_level->lev->required > 0 :
2233                      local_player->gems_still_needed > 0 ||
2234                      local_player->sokobanfields_still_needed > 0 ||
2235                      local_player->lights_still_needed > 0);
2236
2237   UpdatePlayfieldElementCount();
2238
2239   /* update game panel control values */
2240
2241   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2242   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2243
2244   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2245   for (i = 0; i < MAX_NUM_KEYS; i++)
2246     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2247   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2248   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2249
2250   if (game.centered_player_nr == -1)
2251   {
2252     for (i = 0; i < MAX_PLAYERS; i++)
2253     {
2254       for (k = 0; k < MAX_NUM_KEYS; k++)
2255       {
2256         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2257         {
2258           if (level.native_em_level->ply[i]->keys & (1 << k))
2259             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2260               get_key_element_from_nr(k);
2261         }
2262         else if (stored_player[i].key[k])
2263           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2264             get_key_element_from_nr(k);
2265       }
2266
2267       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2268         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2269           level.native_em_level->ply[i]->dynamite;
2270       else
2271         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2272           stored_player[i].inventory_size;
2273
2274       if (stored_player[i].num_white_keys > 0)
2275         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2276           EL_DC_KEY_WHITE;
2277
2278       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2279         stored_player[i].num_white_keys;
2280     }
2281   }
2282   else
2283   {
2284     int player_nr = game.centered_player_nr;
2285
2286     for (k = 0; k < MAX_NUM_KEYS; k++)
2287     {
2288       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289       {
2290         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2291           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2292             get_key_element_from_nr(k);
2293       }
2294       else if (stored_player[player_nr].key[k])
2295         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2296           get_key_element_from_nr(k);
2297     }
2298
2299     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2300       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2301         level.native_em_level->ply[player_nr]->dynamite;
2302     else
2303       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2304         stored_player[player_nr].inventory_size;
2305
2306     if (stored_player[player_nr].num_white_keys > 0)
2307       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2308
2309     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2310       stored_player[player_nr].num_white_keys;
2311   }
2312
2313   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2314   {
2315     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2316       get_inventory_element_from_pos(local_player, i);
2317     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, -i - 1);
2319   }
2320
2321   game_panel_controls[GAME_PANEL_SCORE].value = score;
2322   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2323
2324   game_panel_controls[GAME_PANEL_TIME].value = time;
2325
2326   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2327   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2328   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2329
2330   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2331     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2332      EL_EMPTY);
2333   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2334     local_player->shield_normal_time_left;
2335   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2336     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2337      EL_EMPTY);
2338   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2339     local_player->shield_deadly_time_left;
2340
2341   game_panel_controls[GAME_PANEL_EXIT].value =
2342     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2343
2344   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2345     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2348      EL_EMC_MAGIC_BALL_SWITCH);
2349
2350   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2351     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2353     game.light_time_left;
2354
2355   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2356     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2358     game.timegate_time_left;
2359
2360   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2361     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2362
2363   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2364     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2365   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2366     game.lenses_time_left;
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2369     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2371     game.magnify_time_left;
2372
2373   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2374     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2375      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2376      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2377      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2378      EL_BALLOON_SWITCH_NONE);
2379
2380   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2381     local_player->dynabomb_count;
2382   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2383     local_player->dynabomb_size;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2385     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2386
2387   game_panel_controls[GAME_PANEL_PENGUINS].value =
2388     local_player->friends_still_needed;
2389
2390   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2391     local_player->sokobanfields_still_needed;
2392   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2393     local_player->sokobanfields_still_needed;
2394
2395   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2396     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2397
2398   for (i = 0; i < NUM_BELTS; i++)
2399   {
2400     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2401       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2402        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2403     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2404       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2405   }
2406
2407   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2408     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2410     game.magic_wall_time_left;
2411
2412 #if USE_PLAYER_GRAVITY
2413   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2414     local_player->gravity;
2415 #else
2416   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2417 #endif
2418
2419   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2420     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2421
2422   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2423     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2424       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2425        game.panel.element[i].id : EL_UNDEFINED);
2426
2427   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2428     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2429       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2430        element_info[game.panel.element_count[i].id].element_count : 0);
2431
2432   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2433     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2434       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2435        element_info[game.panel.ce_score[i].id].collect_score : 0);
2436
2437   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2438     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2439       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2440        element_info[game.panel.ce_score_element[i].id].collect_score :
2441        EL_UNDEFINED);
2442
2443   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2444   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2445   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2446
2447   /* update game panel control frames */
2448
2449   for (i = 0; game_panel_controls[i].nr != -1; i++)
2450   {
2451     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2452
2453     if (gpc->type == TYPE_ELEMENT)
2454     {
2455       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2456       {
2457         int last_anim_random_frame = gfx.anim_random_frame;
2458         int element = gpc->value;
2459         int graphic = el2panelimg(element);
2460
2461         if (gpc->value != gpc->last_value)
2462         {
2463           gpc->gfx_frame = 0;
2464           gpc->gfx_random = INIT_GFX_RANDOM();
2465         }
2466         else
2467         {
2468           gpc->gfx_frame++;
2469
2470           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2471               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2472             gpc->gfx_random = INIT_GFX_RANDOM();
2473         }
2474
2475         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2476           gfx.anim_random_frame = gpc->gfx_random;
2477
2478         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2479           gpc->gfx_frame = element_info[element].collect_score;
2480
2481         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2482                                               gpc->gfx_frame);
2483
2484         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2485           gfx.anim_random_frame = last_anim_random_frame;
2486       }
2487     }
2488   }
2489 }
2490
2491 void DisplayGameControlValues()
2492 {
2493   boolean redraw_panel = FALSE;
2494   int i;
2495
2496   for (i = 0; game_panel_controls[i].nr != -1; i++)
2497   {
2498     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2499
2500     if (PANEL_DEACTIVATED(gpc->pos))
2501       continue;
2502
2503     if (gpc->value == gpc->last_value &&
2504         gpc->frame == gpc->last_frame)
2505       continue;
2506
2507     redraw_panel = TRUE;
2508   }
2509
2510   if (!redraw_panel)
2511     return;
2512
2513   /* copy default game door content to main double buffer */
2514   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516
2517   /* redraw game control buttons */
2518 #if 1
2519   RedrawGameButtons();
2520 #else
2521   UnmapGameButtons();
2522   MapGameButtons();
2523 #endif
2524
2525   game_status = GAME_MODE_PSEUDO_PANEL;
2526
2527 #if 1
2528   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2529 #else
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531 #endif
2532   {
2533 #if 1
2534     int nr = game_panel_order[i].nr;
2535     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2536 #else
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538     int nr = gpc->nr;
2539 #endif
2540     struct TextPosInfo *pos = gpc->pos;
2541     int type = gpc->type;
2542     int value = gpc->value;
2543     int frame = gpc->frame;
2544 #if 0
2545     int last_value = gpc->last_value;
2546     int last_frame = gpc->last_frame;
2547 #endif
2548     int size = pos->size;
2549     int font = pos->font;
2550     boolean draw_masked = pos->draw_masked;
2551     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2552
2553     if (PANEL_DEACTIVATED(pos))
2554       continue;
2555
2556 #if 0
2557     if (value == last_value && frame == last_frame)
2558       continue;
2559 #endif
2560
2561     gpc->last_value = value;
2562     gpc->last_frame = frame;
2563
2564 #if 0
2565     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2566 #endif
2567
2568     if (type == TYPE_INTEGER)
2569     {
2570       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571           nr == GAME_PANEL_TIME)
2572       {
2573         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2574
2575         if (use_dynamic_size)           /* use dynamic number of digits */
2576         {
2577           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579           int size2 = size1 + 1;
2580           int font1 = pos->font;
2581           int font2 = pos->font_alt;
2582
2583           size = (value < value_change ? size1 : size2);
2584           font = (value < value_change ? font1 : font2);
2585
2586 #if 0
2587           /* clear background if value just changed its size (dynamic digits) */
2588           if ((last_value < value_change) != (value < value_change))
2589           {
2590             int width1 = size1 * getFontWidth(font1);
2591             int width2 = size2 * getFontWidth(font2);
2592             int max_width = MAX(width1, width2);
2593             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2594
2595             pos->width = max_width;
2596
2597             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598                                        max_width, max_height);
2599           }
2600 #endif
2601         }
2602       }
2603
2604 #if 1
2605       /* correct text size if "digits" is zero or less */
2606       if (size <= 0)
2607         size = strlen(int2str(value, size));
2608
2609       /* dynamically correct text alignment */
2610       pos->width = size * getFontWidth(font);
2611 #endif
2612
2613       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2614                   int2str(value, size), font, mask_mode);
2615     }
2616     else if (type == TYPE_ELEMENT)
2617     {
2618       int element, graphic;
2619       Bitmap *src_bitmap;
2620       int src_x, src_y;
2621       int width, height;
2622       int dst_x = PANEL_XPOS(pos);
2623       int dst_y = PANEL_YPOS(pos);
2624
2625 #if 1
2626       if (value != EL_UNDEFINED && value != EL_EMPTY)
2627       {
2628         element = value;
2629         graphic = el2panelimg(value);
2630
2631         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2632
2633 #if 1
2634         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2635           size = TILESIZE;
2636 #endif
2637
2638         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2639                               &src_x, &src_y);
2640
2641         width  = graphic_info[graphic].width  * size / TILESIZE;
2642         height = graphic_info[graphic].height * size / TILESIZE;
2643
2644         if (draw_masked)
2645         {
2646           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2647                         dst_x - src_x, dst_y - src_y);
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         }
2651         else
2652         {
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655         }
2656       }
2657 #else
2658       if (value == EL_UNDEFINED || value == EL_EMPTY)
2659       {
2660         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2661         graphic = el2panelimg(element);
2662
2663         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2664         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2665         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2666       }
2667       else
2668       {
2669         element = value;
2670         graphic = el2panelimg(value);
2671
2672         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2673       }
2674
2675       width  = graphic_info[graphic].width  * size / TILESIZE;
2676       height = graphic_info[graphic].height * size / TILESIZE;
2677
2678       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2679 #endif
2680     }
2681     else if (type == TYPE_STRING)
2682     {
2683       boolean active = (value != 0);
2684       char *state_normal = "off";
2685       char *state_active = "on";
2686       char *state = (active ? state_active : state_normal);
2687       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2688                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2689                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2690                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2691
2692       if (nr == GAME_PANEL_GRAVITY_STATE)
2693       {
2694         int font1 = pos->font;          /* (used for normal state) */
2695         int font2 = pos->font_alt;      /* (used for active state) */
2696 #if 0
2697         int size1 = strlen(state_normal);
2698         int size2 = strlen(state_active);
2699         int width1 = size1 * getFontWidth(font1);
2700         int width2 = size2 * getFontWidth(font2);
2701         int max_width = MAX(width1, width2);
2702         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2703
2704         pos->width = max_width;
2705
2706         /* clear background for values that may have changed its size */
2707         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2708                                    max_width, max_height);
2709 #endif
2710
2711         font = (active ? font2 : font1);
2712       }
2713
2714       if (s != NULL)
2715       {
2716         char *s_cut;
2717
2718 #if 1
2719         if (size <= 0)
2720         {
2721           /* don't truncate output if "chars" is zero or less */
2722           size = strlen(s);
2723
2724           /* dynamically correct text alignment */
2725           pos->width = size * getFontWidth(font);
2726         }
2727 #endif
2728
2729         s_cut = getStringCopyN(s, size);
2730
2731         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2732                     s_cut, font, mask_mode);
2733
2734         free(s_cut);
2735       }
2736     }
2737
2738     redraw_mask |= REDRAW_DOOR_1;
2739   }
2740
2741   game_status = GAME_MODE_PLAYING;
2742 }
2743
2744 void UpdateAndDisplayGameControlValues()
2745 {
2746   if (tape.warp_forward)
2747     return;
2748
2749   UpdateGameControlValues();
2750   DisplayGameControlValues();
2751 }
2752
2753 void DrawGameValue_Emeralds(int value)
2754 {
2755   struct TextPosInfo *pos = &game.panel.gems;
2756 #if 1
2757   int font_nr = pos->font;
2758 #else
2759   int font_nr = FONT_TEXT_2;
2760 #endif
2761   int font_width = getFontWidth(font_nr);
2762   int chars = pos->size;
2763
2764 #if 1
2765   return;       /* !!! USE NEW STUFF !!! */
2766 #endif
2767
2768   if (PANEL_DEACTIVATED(pos))
2769     return;
2770
2771   pos->width = chars * font_width;
2772
2773   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2774 }
2775
2776 void DrawGameValue_Dynamite(int value)
2777 {
2778   struct TextPosInfo *pos = &game.panel.inventory_count;
2779 #if 1
2780   int font_nr = pos->font;
2781 #else
2782   int font_nr = FONT_TEXT_2;
2783 #endif
2784   int font_width = getFontWidth(font_nr);
2785   int chars = pos->size;
2786
2787 #if 1
2788   return;       /* !!! USE NEW STUFF !!! */
2789 #endif
2790
2791   if (PANEL_DEACTIVATED(pos))
2792     return;
2793
2794   pos->width = chars * font_width;
2795
2796   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2797 }
2798
2799 void DrawGameValue_Score(int value)
2800 {
2801   struct TextPosInfo *pos = &game.panel.score;
2802 #if 1
2803   int font_nr = pos->font;
2804 #else
2805   int font_nr = FONT_TEXT_2;
2806 #endif
2807   int font_width = getFontWidth(font_nr);
2808   int chars = pos->size;
2809
2810 #if 1
2811   return;       /* !!! USE NEW STUFF !!! */
2812 #endif
2813
2814   if (PANEL_DEACTIVATED(pos))
2815     return;
2816
2817   pos->width = chars * font_width;
2818
2819   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2820 }
2821
2822 void DrawGameValue_Time(int value)
2823 {
2824   struct TextPosInfo *pos = &game.panel.time;
2825   static int last_value = -1;
2826   int chars1 = 3;
2827   int chars2 = 4;
2828   int chars = pos->size;
2829 #if 1
2830   int font1_nr = pos->font;
2831   int font2_nr = pos->font_alt;
2832 #else
2833   int font1_nr = FONT_TEXT_2;
2834   int font2_nr = FONT_TEXT_1;
2835 #endif
2836   int font_nr = font1_nr;
2837   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2838
2839 #if 1
2840   return;       /* !!! USE NEW STUFF !!! */
2841 #endif
2842
2843   if (PANEL_DEACTIVATED(pos))
2844     return;
2845
2846   if (use_dynamic_chars)                /* use dynamic number of chars */
2847   {
2848     chars   = (value < 1000 ? chars1   : chars2);
2849     font_nr = (value < 1000 ? font1_nr : font2_nr);
2850   }
2851
2852   /* clear background if value just changed its size (dynamic chars only) */
2853   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2854   {
2855     int width1 = chars1 * getFontWidth(font1_nr);
2856     int width2 = chars2 * getFontWidth(font2_nr);
2857     int max_width = MAX(width1, width2);
2858     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2859
2860     pos->width = max_width;
2861
2862     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2863                                max_width, max_height);
2864   }
2865
2866   pos->width = chars * getFontWidth(font_nr);
2867
2868   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2869
2870   last_value = value;
2871 }
2872
2873 void DrawGameValue_Level(int value)
2874 {
2875   struct TextPosInfo *pos = &game.panel.level_number;
2876   int chars1 = 2;
2877   int chars2 = 3;
2878   int chars = pos->size;
2879 #if 1
2880   int font1_nr = pos->font;
2881   int font2_nr = pos->font_alt;
2882 #else
2883   int font1_nr = FONT_TEXT_2;
2884   int font2_nr = FONT_TEXT_1;
2885 #endif
2886   int font_nr = font1_nr;
2887   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2888
2889 #if 1
2890   return;       /* !!! USE NEW STUFF !!! */
2891 #endif
2892
2893   if (PANEL_DEACTIVATED(pos))
2894     return;
2895
2896   if (use_dynamic_chars)                /* use dynamic number of chars */
2897   {
2898     chars   = (level_nr < 100 ? chars1   : chars2);
2899     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2900   }
2901
2902   pos->width = chars * getFontWidth(font_nr);
2903
2904   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2905 }
2906
2907 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2908 {
2909 #if 0
2910   struct TextPosInfo *pos = &game.panel.keys;
2911 #endif
2912 #if 0
2913   int base_key_graphic = EL_KEY_1;
2914 #endif
2915   int i;
2916
2917 #if 1
2918   return;       /* !!! USE NEW STUFF !!! */
2919 #endif
2920
2921 #if 0
2922   if (PANEL_DEACTIVATED(pos))
2923     return;
2924 #endif
2925
2926 #if 0
2927   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2928     base_key_graphic = EL_EM_KEY_1;
2929 #endif
2930
2931 #if 0
2932   pos->width = 4 * MINI_TILEX;
2933 #endif
2934
2935 #if 1
2936   for (i = 0; i < MAX_NUM_KEYS; i++)
2937 #else
2938   /* currently only 4 of 8 possible keys are displayed */
2939   for (i = 0; i < STD_NUM_KEYS; i++)
2940 #endif
2941   {
2942 #if 1
2943     struct TextPosInfo *pos = &game.panel.key[i];
2944 #endif
2945     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2946     int src_y = DOOR_GFX_PAGEY1 + 123;
2947 #if 1
2948     int dst_x = PANEL_XPOS(pos);
2949     int dst_y = PANEL_YPOS(pos);
2950 #else
2951     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2952     int dst_y = PANEL_YPOS(pos);
2953 #endif
2954
2955 #if 1
2956     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2957                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2958                    EL_KEY_1) + i;
2959     int graphic = el2edimg(element);
2960 #endif
2961
2962 #if 1
2963     if (PANEL_DEACTIVATED(pos))
2964       continue;
2965 #endif
2966
2967 #if 0
2968     /* masked blit with tiles from half-size scaled bitmap does not work yet
2969        (no mask bitmap created for these sizes after loading and scaling) --
2970        solution: load without creating mask, scale, then create final mask */
2971
2972     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2973                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2974
2975     if (key[i])
2976     {
2977 #if 0
2978       int graphic = el2edimg(base_key_graphic + i);
2979 #endif
2980       Bitmap *src_bitmap;
2981       int src_x, src_y;
2982
2983       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2984
2985       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2986                     dst_x - src_x, dst_y - src_y);
2987       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2988                        dst_x, dst_y);
2989     }
2990 #else
2991 #if 1
2992     if (key[i])
2993       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2994     else
2995       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2996                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2997 #else
2998     if (key[i])
2999       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3000     else
3001       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3002                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3003 #endif
3004 #endif
3005   }
3006 }
3007
3008 #else
3009
3010 void DrawGameValue_Emeralds(int value)
3011 {
3012   int font_nr = FONT_TEXT_2;
3013   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3014
3015   if (PANEL_DEACTIVATED(game.panel.gems))
3016     return;
3017
3018   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3019 }
3020
3021 void DrawGameValue_Dynamite(int value)
3022 {
3023   int font_nr = FONT_TEXT_2;
3024   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3025
3026   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3027     return;
3028
3029   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3030 }
3031
3032 void DrawGameValue_Score(int value)
3033 {
3034   int font_nr = FONT_TEXT_2;
3035   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3036
3037   if (PANEL_DEACTIVATED(game.panel.score))
3038     return;
3039
3040   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3041 }
3042
3043 void DrawGameValue_Time(int value)
3044 {
3045   int font1_nr = FONT_TEXT_2;
3046 #if 1
3047   int font2_nr = FONT_TEXT_1;
3048 #else
3049   int font2_nr = FONT_LEVEL_NUMBER;
3050 #endif
3051   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3052   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3053
3054   if (PANEL_DEACTIVATED(game.panel.time))
3055     return;
3056
3057   /* clear background if value just changed its size */
3058   if (value == 999 || value == 1000)
3059     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3060
3061   if (value < 1000)
3062     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3063   else
3064     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3065 }
3066
3067 void DrawGameValue_Level(int value)
3068 {
3069   int font1_nr = FONT_TEXT_2;
3070 #if 1
3071   int font2_nr = FONT_TEXT_1;
3072 #else
3073   int font2_nr = FONT_LEVEL_NUMBER;
3074 #endif
3075
3076   if (PANEL_DEACTIVATED(game.panel.level))
3077     return;
3078
3079   if (level_nr < 100)
3080     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3081   else
3082     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3083 }
3084
3085 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3086 {
3087   int base_key_graphic = EL_KEY_1;
3088   int i;
3089
3090   if (PANEL_DEACTIVATED(game.panel.keys))
3091     return;
3092
3093   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3094     base_key_graphic = EL_EM_KEY_1;
3095
3096   /* currently only 4 of 8 possible keys are displayed */
3097   for (i = 0; i < STD_NUM_KEYS; i++)
3098   {
3099     int x = XX_KEYS + i * MINI_TILEX;
3100     int y = YY_KEYS;
3101
3102     if (key[i])
3103       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3104     else
3105       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3106                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3107   }
3108 }
3109
3110 #endif
3111
3112 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3113                        int key_bits)
3114 {
3115   int key[MAX_NUM_KEYS];
3116   int i;
3117
3118   /* prevent EM engine from updating time/score values parallel to GameWon() */
3119   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3120       local_player->LevelSolved)
3121     return;
3122
3123   for (i = 0; i < MAX_NUM_KEYS; i++)
3124     key[i] = key_bits & (1 << i);
3125
3126   DrawGameValue_Level(level_nr);
3127
3128   DrawGameValue_Emeralds(emeralds);
3129   DrawGameValue_Dynamite(dynamite);
3130   DrawGameValue_Score(score);
3131   DrawGameValue_Time(time);
3132
3133   DrawGameValue_Keys(key);
3134 }
3135
3136 void UpdateGameDoorValues()
3137 {
3138   UpdateGameControlValues();
3139 }
3140
3141 void DrawGameDoorValues()
3142 {
3143   DisplayGameControlValues();
3144 }
3145
3146 void DrawGameDoorValues_OLD()
3147 {
3148   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3149   int dynamite_value = 0;
3150   int score_value = (local_player->LevelSolved ? local_player->score_final :
3151                      local_player->score);
3152   int gems_value = local_player->gems_still_needed;
3153   int key_bits = 0;
3154   int i, j;
3155
3156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3157   {
3158     DrawGameDoorValues_EM();
3159
3160     return;
3161   }
3162
3163   if (game.centered_player_nr == -1)
3164   {
3165     for (i = 0; i < MAX_PLAYERS; i++)
3166     {
3167       for (j = 0; j < MAX_NUM_KEYS; j++)
3168         if (stored_player[i].key[j])
3169           key_bits |= (1 << j);
3170
3171       dynamite_value += stored_player[i].inventory_size;
3172     }
3173   }
3174   else
3175   {
3176     int player_nr = game.centered_player_nr;
3177
3178     for (i = 0; i < MAX_NUM_KEYS; i++)
3179       if (stored_player[player_nr].key[i])
3180         key_bits |= (1 << i);
3181
3182     dynamite_value = stored_player[player_nr].inventory_size;
3183   }
3184
3185   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3186                     key_bits);
3187 }
3188
3189
3190 /*
3191   =============================================================================
3192   InitGameEngine()
3193   -----------------------------------------------------------------------------
3194   initialize game engine due to level / tape version number
3195   =============================================================================
3196 */
3197
3198 static void InitGameEngine()
3199 {
3200   int i, j, k, l, x, y;
3201
3202   /* set game engine from tape file when re-playing, else from level file */
3203   game.engine_version = (tape.playing ? tape.engine_version :
3204                          level.game_version);
3205
3206   /* ---------------------------------------------------------------------- */
3207   /* set flags for bugs and changes according to active game engine version */
3208   /* ---------------------------------------------------------------------- */
3209
3210   /*
3211     Summary of bugfix/change:
3212     Fixed handling for custom elements that change when pushed by the player.
3213
3214     Fixed/changed in version:
3215     3.1.0
3216
3217     Description:
3218     Before 3.1.0, custom elements that "change when pushing" changed directly
3219     after the player started pushing them (until then handled in "DigField()").
3220     Since 3.1.0, these custom elements are not changed until the "pushing"
3221     move of the element is finished (now handled in "ContinueMoving()").
3222
3223     Affected levels/tapes:
3224     The first condition is generally needed for all levels/tapes before version
3225     3.1.0, which might use the old behaviour before it was changed; known tapes
3226     that are affected are some tapes from the level set "Walpurgis Gardens" by
3227     Jamie Cullen.
3228     The second condition is an exception from the above case and is needed for
3229     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3230     above (including some development versions of 3.1.0), but before it was
3231     known that this change would break tapes like the above and was fixed in
3232     3.1.1, so that the changed behaviour was active although the engine version
3233     while recording maybe was before 3.1.0. There is at least one tape that is
3234     affected by this exception, which is the tape for the one-level set "Bug
3235     Machine" by Juergen Bonhagen.
3236   */
3237
3238   game.use_change_when_pushing_bug =
3239     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3240      !(tape.playing &&
3241        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3242        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3243
3244   /*
3245     Summary of bugfix/change:
3246     Fixed handling for blocking the field the player leaves when moving.
3247
3248     Fixed/changed in version:
3249     3.1.1
3250
3251     Description:
3252     Before 3.1.1, when "block last field when moving" was enabled, the field
3253     the player is leaving when moving was blocked for the time of the move,
3254     and was directly unblocked afterwards. This resulted in the last field
3255     being blocked for exactly one less than the number of frames of one player
3256     move. Additionally, even when blocking was disabled, the last field was
3257     blocked for exactly one frame.
3258     Since 3.1.1, due to changes in player movement handling, the last field
3259     is not blocked at all when blocking is disabled. When blocking is enabled,
3260     the last field is blocked for exactly the number of frames of one player
3261     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3262     last field is blocked for exactly one more than the number of frames of
3263     one player move.
3264
3265     Affected levels/tapes:
3266     (!!! yet to be determined -- probably many !!!)
3267   */
3268
3269   game.use_block_last_field_bug =
3270     (game.engine_version < VERSION_IDENT(3,1,1,0));
3271
3272   /*
3273     Summary of bugfix/change:
3274     Changed behaviour of CE changes with multiple changes per single frame.
3275
3276     Fixed/changed in version:
3277     3.2.0-6
3278
3279     Description:
3280     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3281     This resulted in race conditions where CEs seem to behave strange in some
3282     situations (where triggered CE changes were just skipped because there was
3283     already a CE change on that tile in the playfield in that engine frame).
3284     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3285     (The number of changes per frame must be limited in any case, because else
3286     it is easily possible to define CE changes that would result in an infinite
3287     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3288     should be set large enough so that it would only be reached in cases where
3289     the corresponding CE change conditions run into a loop. Therefore, it seems
3290     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3291     maximal number of change pages for custom elements.)
3292
3293     Affected levels/tapes:
3294     Probably many.
3295   */
3296
3297 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3298   game.max_num_changes_per_frame = 1;
3299 #else
3300   game.max_num_changes_per_frame =
3301     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3302 #endif
3303
3304   /* ---------------------------------------------------------------------- */
3305
3306   /* default scan direction: scan playfield from top/left to bottom/right */
3307   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3308
3309   /* dynamically adjust element properties according to game engine version */
3310   InitElementPropertiesEngine(game.engine_version);
3311
3312 #if 0
3313   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3314   printf("          tape version == %06d [%s] [file: %06d]\n",
3315          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3316          tape.file_version);
3317   printf("       => game.engine_version == %06d\n", game.engine_version);
3318 #endif
3319
3320   /* ---------- initialize player's initial move delay --------------------- */
3321
3322   /* dynamically adjust player properties according to level information */
3323   for (i = 0; i < MAX_PLAYERS; i++)
3324     game.initial_move_delay_value[i] =
3325       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3326
3327   /* dynamically adjust player properties according to game engine version */
3328   for (i = 0; i < MAX_PLAYERS; i++)
3329     game.initial_move_delay[i] =
3330       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3331        game.initial_move_delay_value[i] : 0);
3332
3333   /* ---------- initialize player's initial push delay --------------------- */
3334
3335   /* dynamically adjust player properties according to game engine version */
3336   game.initial_push_delay_value =
3337     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3338
3339   /* ---------- initialize changing elements ------------------------------- */
3340
3341   /* initialize changing elements information */
3342   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3343   {
3344     struct ElementInfo *ei = &element_info[i];
3345
3346     /* this pointer might have been changed in the level editor */
3347     ei->change = &ei->change_page[0];
3348
3349     if (!IS_CUSTOM_ELEMENT(i))
3350     {
3351       ei->change->target_element = EL_EMPTY_SPACE;
3352       ei->change->delay_fixed = 0;
3353       ei->change->delay_random = 0;
3354       ei->change->delay_frames = 1;
3355     }
3356
3357     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3358     {
3359       ei->has_change_event[j] = FALSE;
3360
3361       ei->event_page_nr[j] = 0;
3362       ei->event_page[j] = &ei->change_page[0];
3363     }
3364   }
3365
3366   /* add changing elements from pre-defined list */
3367   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3368   {
3369     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3370     struct ElementInfo *ei = &element_info[ch_delay->element];
3371
3372     ei->change->target_element       = ch_delay->target_element;
3373     ei->change->delay_fixed          = ch_delay->change_delay;
3374
3375     ei->change->pre_change_function  = ch_delay->pre_change_function;
3376     ei->change->change_function      = ch_delay->change_function;
3377     ei->change->post_change_function = ch_delay->post_change_function;
3378
3379     ei->change->can_change = TRUE;
3380     ei->change->can_change_or_has_action = TRUE;
3381
3382     ei->has_change_event[CE_DELAY] = TRUE;
3383
3384     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3385     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3386   }
3387
3388   /* ---------- initialize internal run-time variables --------------------- */
3389
3390   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3391   {
3392     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3393
3394     for (j = 0; j < ei->num_change_pages; j++)
3395     {
3396       ei->change_page[j].can_change_or_has_action =
3397         (ei->change_page[j].can_change |
3398          ei->change_page[j].has_action);
3399     }
3400   }
3401
3402   /* add change events from custom element configuration */
3403   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3404   {
3405     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3406
3407     for (j = 0; j < ei->num_change_pages; j++)
3408     {
3409       if (!ei->change_page[j].can_change_or_has_action)
3410         continue;
3411
3412       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3413       {
3414         /* only add event page for the first page found with this event */
3415         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3416         {
3417           ei->has_change_event[k] = TRUE;
3418
3419           ei->event_page_nr[k] = j;
3420           ei->event_page[k] = &ei->change_page[j];
3421         }
3422       }
3423     }
3424   }
3425
3426 #if 1
3427   /* ---------- initialize reference elements in change conditions --------- */
3428
3429   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3430   {
3431     int element = EL_CUSTOM_START + i;
3432     struct ElementInfo *ei = &element_info[element];
3433
3434     for (j = 0; j < ei->num_change_pages; j++)
3435     {
3436       int trigger_element = ei->change_page[j].initial_trigger_element;
3437
3438       if (trigger_element >= EL_PREV_CE_8 &&
3439           trigger_element <= EL_NEXT_CE_8)
3440         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3441
3442       ei->change_page[j].trigger_element = trigger_element;
3443     }
3444   }
3445 #endif
3446
3447   /* ---------- initialize run-time trigger player and element ------------- */
3448
3449   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3450   {
3451     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3452
3453     for (j = 0; j < ei->num_change_pages; j++)
3454     {
3455       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3456       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3457       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3458       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3459       ei->change_page[j].actual_trigger_ce_value = 0;
3460       ei->change_page[j].actual_trigger_ce_score = 0;
3461     }
3462   }
3463
3464   /* ---------- initialize trigger events ---------------------------------- */
3465
3466   /* initialize trigger events information */
3467   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3468     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3469       trigger_events[i][j] = FALSE;
3470
3471   /* add trigger events from element change event properties */
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473   {
3474     struct ElementInfo *ei = &element_info[i];
3475
3476     for (j = 0; j < ei->num_change_pages; j++)
3477     {
3478       if (!ei->change_page[j].can_change_or_has_action)
3479         continue;
3480
3481       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3482       {
3483         int trigger_element = ei->change_page[j].trigger_element;
3484
3485         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3486         {
3487           if (ei->change_page[j].has_event[k])
3488           {
3489             if (IS_GROUP_ELEMENT(trigger_element))
3490             {
3491               struct ElementGroupInfo *group =
3492                 element_info[trigger_element].group;
3493
3494               for (l = 0; l < group->num_elements_resolved; l++)
3495                 trigger_events[group->element_resolved[l]][k] = TRUE;
3496             }
3497             else if (trigger_element == EL_ANY_ELEMENT)
3498               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3499                 trigger_events[l][k] = TRUE;
3500             else
3501               trigger_events[trigger_element][k] = TRUE;
3502           }
3503         }
3504       }
3505     }
3506   }
3507
3508   /* ---------- initialize push delay -------------------------------------- */
3509
3510   /* initialize push delay values to default */
3511   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3512   {
3513     if (!IS_CUSTOM_ELEMENT(i))
3514     {
3515       /* set default push delay values (corrected since version 3.0.7-1) */
3516       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3517       {
3518         element_info[i].push_delay_fixed = 2;
3519         element_info[i].push_delay_random = 8;
3520       }
3521       else
3522       {
3523         element_info[i].push_delay_fixed = 8;
3524         element_info[i].push_delay_random = 8;
3525       }
3526     }
3527   }
3528
3529   /* set push delay value for certain elements from pre-defined list */
3530   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3531   {
3532     int e = push_delay_list[i].element;
3533
3534     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3535     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3536   }
3537
3538   /* set push delay value for Supaplex elements for newer engine versions */
3539   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3540   {
3541     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3542     {
3543       if (IS_SP_ELEMENT(i))
3544       {
3545         /* set SP push delay to just enough to push under a falling zonk */
3546         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3547
3548         element_info[i].push_delay_fixed  = delay;
3549         element_info[i].push_delay_random = 0;
3550       }
3551     }
3552   }
3553
3554   /* ---------- initialize move stepsize ----------------------------------- */
3555
3556   /* initialize move stepsize values to default */
3557   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558     if (!IS_CUSTOM_ELEMENT(i))
3559       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3560
3561   /* set move stepsize value for certain elements from pre-defined list */
3562   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3563   {
3564     int e = move_stepsize_list[i].element;
3565
3566     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3567   }
3568
3569   /* ---------- initialize collect score ----------------------------------- */
3570
3571   /* initialize collect score values for custom elements from initial value */
3572   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3573     if (IS_CUSTOM_ELEMENT(i))
3574       element_info[i].collect_score = element_info[i].collect_score_initial;
3575
3576   /* ---------- initialize collect count ----------------------------------- */
3577
3578   /* initialize collect count values for non-custom elements */
3579   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3580     if (!IS_CUSTOM_ELEMENT(i))
3581       element_info[i].collect_count_initial = 0;
3582
3583   /* add collect count values for all elements from pre-defined list */
3584   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3585     element_info[collect_count_list[i].element].collect_count_initial =
3586       collect_count_list[i].count;
3587
3588   /* ---------- initialize access direction -------------------------------- */
3589
3590   /* initialize access direction values to default (access from every side) */
3591   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3592     if (!IS_CUSTOM_ELEMENT(i))
3593       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3594
3595   /* set access direction value for certain elements from pre-defined list */
3596   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3597     element_info[access_direction_list[i].element].access_direction =
3598       access_direction_list[i].direction;
3599
3600   /* ---------- initialize explosion content ------------------------------- */
3601   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3602   {
3603     if (IS_CUSTOM_ELEMENT(i))
3604       continue;
3605
3606     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3607     {
3608       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3609
3610       element_info[i].content.e[x][y] =
3611         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3612          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3613          i == EL_PLAYER_3 ? EL_EMERALD :
3614          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3615          i == EL_MOLE ? EL_EMERALD_RED :
3616          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3617          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3618          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3619          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3620          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3621          i == EL_WALL_EMERALD ? EL_EMERALD :
3622          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3623          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3624          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3625          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3626          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3627          i == EL_WALL_PEARL ? EL_PEARL :
3628          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3629          EL_EMPTY);
3630     }
3631   }
3632
3633   /* ---------- initialize recursion detection ------------------------------ */
3634   recursion_loop_depth = 0;
3635   recursion_loop_detected = FALSE;
3636   recursion_loop_element = EL_UNDEFINED;
3637
3638   /* ---------- initialize graphics engine ---------------------------------- */
3639   game.scroll_delay_value =
3640     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3641      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3642   game.scroll_delay_value =
3643     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3644 }
3645
3646 int get_num_special_action(int element, int action_first, int action_last)
3647 {
3648   int num_special_action = 0;
3649   int i, j;
3650
3651   for (i = action_first; i <= action_last; i++)
3652   {
3653     boolean found = FALSE;
3654
3655     for (j = 0; j < NUM_DIRECTIONS; j++)
3656       if (el_act_dir2img(element, i, j) !=
3657           el_act_dir2img(element, ACTION_DEFAULT, j))
3658         found = TRUE;
3659
3660     if (found)
3661       num_special_action++;
3662     else
3663       break;
3664   }
3665
3666   return num_special_action;
3667 }
3668
3669
3670 /*
3671   =============================================================================
3672   InitGame()
3673   -----------------------------------------------------------------------------
3674   initialize and start new game
3675   =============================================================================
3676 */
3677
3678 void InitGame()
3679 {
3680   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3681   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3682   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3683 #if 0
3684   boolean do_fading = (game_status == GAME_MODE_MAIN);
3685 #endif
3686 #if 1
3687   int initial_move_dir = MV_DOWN;
3688 #else
3689   int initial_move_dir = MV_NONE;
3690 #endif
3691   int i, j, x, y;
3692
3693   game_status = GAME_MODE_PLAYING;
3694
3695   InitGameEngine();
3696   InitGameControlValues();
3697
3698   /* don't play tapes over network */
3699   network_playing = (options.network && !tape.playing);
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     struct PlayerInfo *player = &stored_player[i];
3704
3705     player->index_nr = i;
3706     player->index_bit = (1 << i);
3707     player->element_nr = EL_PLAYER_1 + i;
3708
3709     player->present = FALSE;
3710     player->active = FALSE;
3711     player->killed = FALSE;
3712     player->reanimated = FALSE;
3713
3714     player->action = 0;
3715     player->effective_action = 0;
3716     player->programmed_action = 0;
3717
3718     player->score = 0;
3719     player->score_final = 0;
3720
3721     player->gems_still_needed = level.gems_needed;
3722     player->sokobanfields_still_needed = 0;
3723     player->lights_still_needed = 0;
3724     player->friends_still_needed = 0;
3725
3726     for (j = 0; j < MAX_NUM_KEYS; j++)
3727       player->key[j] = FALSE;
3728
3729     player->num_white_keys = 0;
3730
3731     player->dynabomb_count = 0;
3732     player->dynabomb_size = 1;
3733     player->dynabombs_left = 0;
3734     player->dynabomb_xl = FALSE;
3735
3736     player->MovDir = initial_move_dir;
3737     player->MovPos = 0;
3738     player->GfxPos = 0;
3739     player->GfxDir = initial_move_dir;
3740     player->GfxAction = ACTION_DEFAULT;
3741     player->Frame = 0;
3742     player->StepFrame = 0;
3743
3744     player->initial_element = player->element_nr;
3745     player->artwork_element =
3746       (level.use_artwork_element[i] ? level.artwork_element[i] :
3747        player->element_nr);
3748     player->use_murphy = FALSE;
3749
3750     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3751     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3752
3753     player->gravity = level.initial_player_gravity[i];
3754
3755     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3756
3757     player->actual_frame_counter = 0;
3758
3759     player->step_counter = 0;
3760
3761     player->last_move_dir = initial_move_dir;
3762
3763     player->is_active = FALSE;
3764
3765     player->is_waiting = FALSE;
3766     player->is_moving = FALSE;
3767     player->is_auto_moving = FALSE;
3768     player->is_digging = FALSE;
3769     player->is_snapping = FALSE;
3770     player->is_collecting = FALSE;
3771     player->is_pushing = FALSE;
3772     player->is_switching = FALSE;
3773     player->is_dropping = FALSE;
3774     player->is_dropping_pressed = FALSE;
3775
3776     player->is_bored = FALSE;
3777     player->is_sleeping = FALSE;
3778
3779     player->frame_counter_bored = -1;
3780     player->frame_counter_sleeping = -1;
3781
3782     player->anim_delay_counter = 0;
3783     player->post_delay_counter = 0;
3784
3785     player->dir_waiting = initial_move_dir;
3786     player->action_waiting = ACTION_DEFAULT;
3787     player->last_action_waiting = ACTION_DEFAULT;
3788     player->special_action_bored = ACTION_DEFAULT;
3789     player->special_action_sleeping = ACTION_DEFAULT;
3790
3791     player->switch_x = -1;
3792     player->switch_y = -1;
3793
3794     player->drop_x = -1;
3795     player->drop_y = -1;
3796
3797     player->show_envelope = 0;
3798
3799     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3800
3801     player->push_delay       = -1;      /* initialized when pushing starts */
3802     player->push_delay_value = game.initial_push_delay_value;
3803
3804     player->drop_delay = 0;
3805     player->drop_pressed_delay = 0;
3806
3807     player->last_jx = -1;
3808     player->last_jy = -1;
3809     player->jx = -1;
3810     player->jy = -1;
3811
3812     player->shield_normal_time_left = 0;
3813     player->shield_deadly_time_left = 0;
3814
3815     player->inventory_infinite_element = EL_UNDEFINED;
3816     player->inventory_size = 0;
3817
3818     if (level.use_initial_inventory[i])
3819     {
3820       for (j = 0; j < level.initial_inventory_size[i]; j++)
3821       {
3822         int element = level.initial_inventory_content[i][j];
3823         int collect_count = element_info[element].collect_count_initial;
3824         int k;
3825
3826         if (!IS_CUSTOM_ELEMENT(element))
3827           collect_count = 1;
3828
3829         if (collect_count == 0)
3830           player->inventory_infinite_element = element;
3831         else
3832           for (k = 0; k < collect_count; k++)
3833             if (player->inventory_size < MAX_INVENTORY_SIZE)
3834               player->inventory_element[player->inventory_size++] = element;
3835       }
3836     }
3837
3838     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3839     SnapField(player, 0, 0);
3840
3841     player->LevelSolved = FALSE;
3842     player->GameOver = FALSE;
3843
3844     player->LevelSolved_GameWon = FALSE;
3845     player->LevelSolved_GameEnd = FALSE;
3846     player->LevelSolved_PanelOff = FALSE;
3847     player->LevelSolved_SaveTape = FALSE;
3848     player->LevelSolved_SaveScore = FALSE;
3849     player->LevelSolved_CountingTime = 0;
3850     player->LevelSolved_CountingScore = 0;
3851   }
3852
3853   network_player_action_received = FALSE;
3854
3855 #if defined(NETWORK_AVALIABLE)
3856   /* initial null action */
3857   if (network_playing)
3858     SendToServer_MovePlayer(MV_NONE);
3859 #endif
3860
3861   ZX = ZY = -1;
3862   ExitX = ExitY = -1;
3863
3864   FrameCounter = 0;
3865   TimeFrames = 0;
3866   TimePlayed = 0;
3867   TimeLeft = level.time;
3868   TapeTime = 0;
3869
3870   ScreenMovDir = MV_NONE;
3871   ScreenMovPos = 0;
3872   ScreenGfxPos = 0;
3873
3874   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3875
3876   AllPlayersGone = FALSE;
3877
3878   game.yamyam_content_nr = 0;
3879   game.robot_wheel_active = FALSE;
3880   game.magic_wall_active = FALSE;
3881   game.magic_wall_time_left = 0;
3882   game.light_time_left = 0;
3883   game.timegate_time_left = 0;
3884   game.switchgate_pos = 0;
3885   game.wind_direction = level.wind_direction_initial;
3886
3887 #if !USE_PLAYER_GRAVITY
3888   game.gravity = FALSE;
3889   game.explosions_delayed = TRUE;
3890 #endif
3891
3892   game.lenses_time_left = 0;
3893   game.magnify_time_left = 0;
3894
3895   game.ball_state = level.ball_state_initial;
3896   game.ball_content_nr = 0;
3897
3898   game.envelope_active = FALSE;
3899
3900   /* set focus to local player for network games, else to all players */
3901   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3902   game.centered_player_nr_next = game.centered_player_nr;
3903   game.set_centered_player = FALSE;
3904
3905   if (network_playing && tape.recording)
3906   {
3907     /* store client dependent player focus when recording network games */
3908     tape.centered_player_nr_next = game.centered_player_nr_next;
3909     tape.set_centered_player = TRUE;
3910   }
3911
3912   for (i = 0; i < NUM_BELTS; i++)
3913   {
3914     game.belt_dir[i] = MV_NONE;
3915     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3916   }
3917
3918   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3919     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3920
3921   SCAN_PLAYFIELD(x, y)
3922   {
3923     Feld[x][y] = level.field[x][y];
3924     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3925     ChangeDelay[x][y] = 0;
3926     ChangePage[x][y] = -1;
3927 #if USE_NEW_CUSTOM_VALUE
3928     CustomValue[x][y] = 0;              /* initialized in InitField() */
3929 #endif
3930     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3931     AmoebaNr[x][y] = 0;
3932     WasJustMoving[x][y] = 0;
3933     WasJustFalling[x][y] = 0;
3934     CheckCollision[x][y] = 0;
3935     CheckImpact[x][y] = 0;
3936     Stop[x][y] = FALSE;
3937     Pushed[x][y] = FALSE;
3938
3939     ChangeCount[x][y] = 0;
3940     ChangeEvent[x][y] = -1;
3941
3942     ExplodePhase[x][y] = 0;
3943     ExplodeDelay[x][y] = 0;
3944     ExplodeField[x][y] = EX_TYPE_NONE;
3945
3946     RunnerVisit[x][y] = 0;
3947     PlayerVisit[x][y] = 0;
3948
3949     GfxFrame[x][y] = 0;
3950     GfxRandom[x][y] = INIT_GFX_RANDOM();
3951     GfxElement[x][y] = EL_UNDEFINED;
3952     GfxAction[x][y] = ACTION_DEFAULT;
3953     GfxDir[x][y] = MV_NONE;
3954     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3955   }
3956
3957   SCAN_PLAYFIELD(x, y)
3958   {
3959     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3960       emulate_bd = FALSE;
3961     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3962       emulate_sb = FALSE;
3963     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3964       emulate_sp = FALSE;
3965
3966     InitField(x, y, TRUE);
3967
3968     ResetGfxAnimation(x, y);
3969   }
3970
3971   InitBeltMovement();
3972
3973   for (i = 0; i < MAX_PLAYERS; i++)
3974   {
3975     struct PlayerInfo *player = &stored_player[i];
3976
3977     /* set number of special actions for bored and sleeping animation */
3978     player->num_special_action_bored =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_BORING_1, ACTION_BORING_LAST);
3981     player->num_special_action_sleeping =
3982       get_num_special_action(player->artwork_element,
3983                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3984   }
3985
3986   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3987                     emulate_sb ? EMU_SOKOBAN :
3988                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3989
3990 #if USE_NEW_ALL_SLIPPERY
3991   /* initialize type of slippery elements */
3992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3993   {
3994     if (!IS_CUSTOM_ELEMENT(i))
3995     {
3996       /* default: elements slip down either to the left or right randomly */
3997       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3998
3999       /* SP style elements prefer to slip down on the left side */
4000       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4001         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4002
4003       /* BD style elements prefer to slip down on the left side */
4004       if (game.emulation == EMU_BOULDERDASH)
4005         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4006     }
4007   }
4008 #endif
4009
4010   /* initialize explosion and ignition delay */
4011   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4012   {
4013     if (!IS_CUSTOM_ELEMENT(i))
4014     {
4015       int num_phase = 8;
4016       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4017                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4018                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4019       int last_phase = (num_phase + 1) * delay;
4020       int half_phase = (num_phase / 2) * delay;
4021
4022       element_info[i].explosion_delay = last_phase - 1;
4023       element_info[i].ignition_delay = half_phase;
4024
4025       if (i == EL_BLACK_ORB)
4026         element_info[i].ignition_delay = 1;
4027     }
4028
4029 #if 0
4030     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4031       element_info[i].explosion_delay = 1;
4032
4033     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4034       element_info[i].ignition_delay = 1;
4035 #endif
4036   }
4037
4038   /* correct non-moving belts to start moving left */
4039   for (i = 0; i < NUM_BELTS; i++)
4040     if (game.belt_dir[i] == MV_NONE)
4041       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4042
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044   /* check if any connected player was not found in playfield */
4045   for (i = 0; i < MAX_PLAYERS; i++)
4046   {
4047     struct PlayerInfo *player = &stored_player[i];
4048
4049     if (player->connected && !player->present)
4050     {
4051       for (j = 0; j < MAX_PLAYERS; j++)
4052       {
4053         struct PlayerInfo *some_player = &stored_player[j];
4054         int jx = some_player->jx, jy = some_player->jy;
4055
4056         /* assign first free player found that is present in the playfield */
4057         if (some_player->present && !some_player->connected)
4058         {
4059           player->present = FALSE;
4060           player->active = FALSE;
4061
4062           some_player->present = TRUE;
4063           some_player->active = TRUE;
4064
4065           /*
4066           player->initial_element = some_player->initial_element;
4067           player->artwork_element = some_player->artwork_element;
4068
4069           player->block_last_field       = some_player->block_last_field;
4070           player->block_delay_adjustment = some_player->block_delay_adjustment;
4071           */
4072
4073           StorePlayer[jx][jy] = some_player->element_nr;
4074
4075           some_player->jx = some_player->last_jx = jx;
4076           some_player->jy = some_player->last_jy = jy;
4077
4078           if (local_player == player)
4079             local_player = some_player;
4080
4081           break;
4082         }
4083       }
4084     }
4085   }
4086
4087 #else
4088
4089   /* check if any connected player was not found in playfield */
4090   for (i = 0; i < MAX_PLAYERS; i++)
4091   {
4092     struct PlayerInfo *player = &stored_player[i];
4093
4094     if (player->connected && !player->present)
4095     {
4096       for (j = 0; j < MAX_PLAYERS; j++)
4097       {
4098         struct PlayerInfo *some_player = &stored_player[j];
4099         int jx = some_player->jx, jy = some_player->jy;
4100
4101         /* assign first free player found that is present in the playfield */
4102         if (some_player->present && !some_player->connected)
4103         {
4104           player->present = TRUE;
4105           player->active = TRUE;
4106
4107           some_player->present = FALSE;
4108           some_player->active = FALSE;
4109
4110           player->initial_element = some_player->initial_element;
4111           player->artwork_element = some_player->artwork_element;
4112
4113           player->block_last_field       = some_player->block_last_field;
4114           player->block_delay_adjustment = some_player->block_delay_adjustment;
4115
4116           StorePlayer[jx][jy] = player->element_nr;
4117
4118           player->jx = player->last_jx = jx;
4119           player->jy = player->last_jy = jy;
4120
4121           break;
4122         }
4123       }
4124     }
4125   }
4126 #endif
4127
4128   if (tape.playing)
4129   {
4130     /* when playing a tape, eliminate all players who do not participate */
4131
4132     for (i = 0; i < MAX_PLAYERS; i++)
4133     {
4134       if (stored_player[i].active && !tape.player_participates[i])
4135       {
4136         struct PlayerInfo *player = &stored_player[i];
4137         int jx = player->jx, jy = player->jy;
4138
4139         player->active = FALSE;
4140         StorePlayer[jx][jy] = 0;
4141         Feld[jx][jy] = EL_EMPTY;
4142       }
4143     }
4144   }
4145   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4146   {
4147     /* when in single player mode, eliminate all but the first active player */
4148
4149     for (i = 0; i < MAX_PLAYERS; i++)
4150     {
4151       if (stored_player[i].active)
4152       {
4153         for (j = i + 1; j < MAX_PLAYERS; j++)
4154         {
4155           if (stored_player[j].active)
4156           {
4157             struct PlayerInfo *player = &stored_player[j];
4158             int jx = player->jx, jy = player->jy;
4159
4160             player->active = FALSE;
4161             player->present = FALSE;
4162
4163             StorePlayer[jx][jy] = 0;
4164             Feld[jx][jy] = EL_EMPTY;
4165           }
4166         }
4167       }
4168     }
4169   }
4170
4171   /* when recording the game, store which players take part in the game */
4172   if (tape.recording)
4173   {
4174     for (i = 0; i < MAX_PLAYERS; i++)
4175       if (stored_player[i].active)
4176         tape.player_participates[i] = TRUE;
4177   }
4178
4179   if (options.debug)
4180   {
4181     for (i = 0; i < MAX_PLAYERS; i++)
4182     {
4183       struct PlayerInfo *player = &stored_player[i];
4184
4185       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4186              i+1,
4187              player->present,
4188              player->connected,
4189              player->active);
4190       if (local_player == player)
4191         printf("Player  %d is local player.\n", i+1);
4192     }
4193   }
4194
4195   if (BorderElement == EL_EMPTY)
4196   {
4197     SBX_Left = 0;
4198     SBX_Right = lev_fieldx - SCR_FIELDX;
4199     SBY_Upper = 0;
4200     SBY_Lower = lev_fieldy - SCR_FIELDY;
4201   }
4202   else
4203   {
4204     SBX_Left = -1;
4205     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4206     SBY_Upper = -1;
4207     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4208   }
4209
4210   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4211     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4212
4213   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4214     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4215
4216   /* if local player not found, look for custom element that might create
4217      the player (make some assumptions about the right custom element) */
4218   if (!local_player->present)
4219   {
4220     int start_x = 0, start_y = 0;
4221     int found_rating = 0;
4222     int found_element = EL_UNDEFINED;
4223     int player_nr = local_player->index_nr;
4224
4225     SCAN_PLAYFIELD(x, y)
4226     {
4227       int element = Feld[x][y];
4228       int content;
4229       int xx, yy;
4230       boolean is_player;
4231
4232       if (level.use_start_element[player_nr] &&
4233           level.start_element[player_nr] == element &&
4234           found_rating < 4)
4235       {
4236         start_x = x;
4237         start_y = y;
4238
4239         found_rating = 4;
4240         found_element = element;
4241       }
4242
4243       if (!IS_CUSTOM_ELEMENT(element))
4244         continue;
4245
4246       if (CAN_CHANGE(element))
4247       {
4248         for (i = 0; i < element_info[element].num_change_pages; i++)
4249         {
4250           /* check for player created from custom element as single target */
4251           content = element_info[element].change_page[i].target_element;
4252           is_player = ELEM_IS_PLAYER(content);
4253
4254           if (is_player && (found_rating < 3 ||
4255                             (found_rating == 3 && element < found_element)))
4256           {
4257             start_x = x;
4258             start_y = y;
4259
4260             found_rating = 3;
4261             found_element = element;
4262           }
4263         }
4264       }
4265
4266       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4267       {
4268         /* check for player created from custom element as explosion content */
4269         content = element_info[element].content.e[xx][yy];
4270         is_player = ELEM_IS_PLAYER(content);
4271
4272         if (is_player && (found_rating < 2 ||
4273                           (found_rating == 2 && element < found_element)))
4274         {
4275           start_x = x + xx - 1;
4276           start_y = y + yy - 1;
4277
4278           found_rating = 2;
4279           found_element = element;
4280         }
4281
4282         if (!CAN_CHANGE(element))
4283           continue;
4284
4285         for (i = 0; i < element_info[element].num_change_pages; i++)
4286         {
4287           /* check for player created from custom element as extended target */
4288           content =
4289             element_info[element].change_page[i].target_content.e[xx][yy];
4290
4291           is_player = ELEM_IS_PLAYER(content);
4292
4293           if (is_player && (found_rating < 1 ||
4294                             (found_rating == 1 && element < found_element)))
4295           {
4296             start_x = x + xx - 1;
4297             start_y = y + yy - 1;
4298
4299             found_rating = 1;
4300             found_element = element;
4301           }
4302         }
4303       }
4304     }
4305
4306     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4307                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4308                 start_x - MIDPOSX);
4309
4310     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4311                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4312                 start_y - MIDPOSY);
4313   }
4314   else
4315   {
4316     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4317                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4318                 local_player->jx - MIDPOSX);
4319
4320     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4321                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4322                 local_player->jy - MIDPOSY);
4323   }
4324
4325 #if 0
4326   /* do not use PLAYING mask for fading out from main screen */
4327   game_status = GAME_MODE_MAIN;
4328 #endif
4329
4330   StopAnimation();
4331
4332   if (!game.restart_level)
4333     CloseDoor(DOOR_CLOSE_1);
4334
4335 #if 1
4336   if (level_editor_test_game)
4337     FadeSkipNextFadeIn();
4338   else
4339     FadeSetEnterScreen();
4340 #else
4341   if (level_editor_test_game)
4342     fading = fading_none;
4343   else
4344     fading = menu.destination;
4345 #endif
4346
4347 #if 1
4348   FadeOut(REDRAW_FIELD);
4349 #else
4350   if (do_fading)
4351     FadeOut(REDRAW_FIELD);
4352 #endif
4353
4354 #if 0
4355   game_status = GAME_MODE_PLAYING;
4356 #endif
4357
4358   /* !!! FIX THIS (START) !!! */
4359   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4360   {
4361     InitGameEngine_EM();
4362
4363     /* blit playfield from scroll buffer to normal back buffer for fading in */
4364     BlitScreenToBitmap_EM(backbuffer);
4365   }
4366   else
4367   {
4368     DrawLevel();
4369     DrawAllPlayers();
4370
4371     /* after drawing the level, correct some elements */
4372     if (game.timegate_time_left == 0)
4373       CloseAllOpenTimegates();
4374
4375     /* blit playfield from scroll buffer to normal back buffer for fading in */
4376     if (setup.soft_scrolling)
4377       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4378
4379     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4380   }
4381   /* !!! FIX THIS (END) !!! */
4382
4383 #if 1
4384   FadeIn(REDRAW_FIELD);
4385 #else
4386   if (do_fading)
4387     FadeIn(REDRAW_FIELD);
4388
4389   BackToFront();
4390 #endif
4391
4392   if (!game.restart_level)
4393   {
4394     /* copy default game door content to main double buffer */
4395     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4396                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4397   }
4398
4399   SetPanelBackground();
4400   SetDrawBackgroundMask(REDRAW_DOOR_1);
4401
4402 #if 1
4403   UpdateAndDisplayGameControlValues();
4404 #else
4405   UpdateGameDoorValues();
4406   DrawGameDoorValues();
4407 #endif
4408
4409   if (!game.restart_level)
4410   {
4411     UnmapGameButtons();
4412     UnmapTapeButtons();
4413     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4414     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4415     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4416     MapGameButtons();
4417     MapTapeButtons();
4418
4419     /* copy actual game door content to door double buffer for OpenDoor() */
4420     BlitBitmap(drawto, bitmap_db_door,
4421                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4422
4423     OpenDoor(DOOR_OPEN_ALL);
4424
4425     PlaySound(SND_GAME_STARTING);
4426
4427     if (setup.sound_music)
4428       PlayLevelMusic();
4429
4430     KeyboardAutoRepeatOffUnlessAutoplay();
4431
4432     if (options.debug)
4433     {
4434       for (i = 0; i < MAX_PLAYERS; i++)
4435         printf("Player %d %sactive.\n",
4436                i + 1, (stored_player[i].active ? "" : "not "));
4437     }
4438   }
4439
4440 #if 1
4441   UnmapAllGadgets();
4442
4443   MapGameButtons();
4444   MapTapeButtons();
4445 #endif
4446
4447   game.restart_level = FALSE;
4448 }
4449
4450 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4451 {
4452   /* this is used for non-R'n'D game engines to update certain engine values */
4453
4454   /* needed to determine if sounds are played within the visible screen area */
4455   scroll_x = actual_scroll_x;
4456   scroll_y = actual_scroll_y;
4457 }
4458
4459 void InitMovDir(int x, int y)
4460 {
4461   int i, element = Feld[x][y];
4462   static int xy[4][2] =
4463   {
4464     {  0, +1 },
4465     { +1,  0 },
4466     {  0, -1 },
4467     { -1,  0 }
4468   };
4469   static int direction[3][4] =
4470   {
4471     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4472     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4473     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4474   };
4475
4476   switch (element)
4477   {
4478     case EL_BUG_RIGHT:
4479     case EL_BUG_UP:
4480     case EL_BUG_LEFT:
4481     case EL_BUG_DOWN:
4482       Feld[x][y] = EL_BUG;
4483       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4484       break;
4485
4486     case EL_SPACESHIP_RIGHT:
4487     case EL_SPACESHIP_UP:
4488     case EL_SPACESHIP_LEFT:
4489     case EL_SPACESHIP_DOWN:
4490       Feld[x][y] = EL_SPACESHIP;
4491       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4492       break;
4493
4494     case EL_BD_BUTTERFLY_RIGHT:
4495     case EL_BD_BUTTERFLY_UP:
4496     case EL_BD_BUTTERFLY_LEFT:
4497     case EL_BD_BUTTERFLY_DOWN:
4498       Feld[x][y] = EL_BD_BUTTERFLY;
4499       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4500       break;
4501
4502     case EL_BD_FIREFLY_RIGHT:
4503     case EL_BD_FIREFLY_UP:
4504     case EL_BD_FIREFLY_LEFT:
4505     case EL_BD_FIREFLY_DOWN:
4506       Feld[x][y] = EL_BD_FIREFLY;
4507       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4508       break;
4509
4510     case EL_PACMAN_RIGHT:
4511     case EL_PACMAN_UP:
4512     case EL_PACMAN_LEFT:
4513     case EL_PACMAN_DOWN:
4514       Feld[x][y] = EL_PACMAN;
4515       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4516       break;
4517
4518     case EL_YAMYAM_LEFT:
4519     case EL_YAMYAM_RIGHT:
4520     case EL_YAMYAM_UP:
4521     case EL_YAMYAM_DOWN:
4522       Feld[x][y] = EL_YAMYAM;
4523       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4524       break;
4525
4526     case EL_SP_SNIKSNAK:
4527       MovDir[x][y] = MV_UP;
4528       break;
4529
4530     case EL_SP_ELECTRON:
4531       MovDir[x][y] = MV_LEFT;
4532       break;
4533
4534     case EL_MOLE_LEFT:
4535     case EL_MOLE_RIGHT:
4536     case EL_MOLE_UP:
4537     case EL_MOLE_DOWN:
4538       Feld[x][y] = EL_MOLE;
4539       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4540       break;
4541
4542     default:
4543       if (IS_CUSTOM_ELEMENT(element))
4544       {
4545         struct ElementInfo *ei = &element_info[element];
4546         int move_direction_initial = ei->move_direction_initial;
4547         int move_pattern = ei->move_pattern;
4548
4549         if (move_direction_initial == MV_START_PREVIOUS)
4550         {
4551           if (MovDir[x][y] != MV_NONE)
4552             return;
4553
4554           move_direction_initial = MV_START_AUTOMATIC;
4555         }
4556
4557         if (move_direction_initial == MV_START_RANDOM)
4558           MovDir[x][y] = 1 << RND(4);
4559         else if (move_direction_initial & MV_ANY_DIRECTION)
4560           MovDir[x][y] = move_direction_initial;
4561         else if (move_pattern == MV_ALL_DIRECTIONS ||
4562                  move_pattern == MV_TURNING_LEFT ||
4563                  move_pattern == MV_TURNING_RIGHT ||
4564                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4565                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4566                  move_pattern == MV_TURNING_RANDOM)
4567           MovDir[x][y] = 1 << RND(4);
4568         else if (move_pattern == MV_HORIZONTAL)
4569           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4570         else if (move_pattern == MV_VERTICAL)
4571           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4572         else if (move_pattern & MV_ANY_DIRECTION)
4573           MovDir[x][y] = element_info[element].move_pattern;
4574         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4575                  move_pattern == MV_ALONG_RIGHT_SIDE)
4576         {
4577           /* use random direction as default start direction */
4578           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4579             MovDir[x][y] = 1 << RND(4);
4580
4581           for (i = 0; i < NUM_DIRECTIONS; i++)
4582           {
4583             int x1 = x + xy[i][0];
4584             int y1 = y + xy[i][1];
4585
4586             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4587             {
4588               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4589                 MovDir[x][y] = direction[0][i];
4590               else
4591                 MovDir[x][y] = direction[1][i];
4592
4593               break;
4594             }
4595           }
4596         }                
4597       }
4598       else
4599       {
4600         MovDir[x][y] = 1 << RND(4);
4601
4602         if (element != EL_BUG &&
4603             element != EL_SPACESHIP &&
4604             element != EL_BD_BUTTERFLY &&
4605             element != EL_BD_FIREFLY)
4606           break;
4607
4608         for (i = 0; i < NUM_DIRECTIONS; i++)
4609         {
4610           int x1 = x + xy[i][0];
4611           int y1 = y + xy[i][1];
4612
4613           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4614           {
4615             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4616             {
4617               MovDir[x][y] = direction[0][i];
4618               break;
4619             }
4620             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4621                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4622             {
4623               MovDir[x][y] = direction[1][i];
4624               break;
4625             }
4626           }
4627         }
4628       }
4629       break;
4630   }
4631
4632   GfxDir[x][y] = MovDir[x][y];
4633 }
4634
4635 void InitAmoebaNr(int x, int y)
4636 {
4637   int i;
4638   int group_nr = AmoebeNachbarNr(x, y);
4639
4640   if (group_nr == 0)
4641   {
4642     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4643     {
4644       if (AmoebaCnt[i] == 0)
4645       {
4646         group_nr = i;
4647         break;
4648       }
4649     }
4650   }
4651
4652   AmoebaNr[x][y] = group_nr;
4653   AmoebaCnt[group_nr]++;
4654   AmoebaCnt2[group_nr]++;
4655 }
4656
4657 static void PlayerWins(struct PlayerInfo *player)
4658 {
4659   player->LevelSolved = TRUE;
4660   player->GameOver = TRUE;
4661
4662   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4663                          level.native_em_level->lev->score : player->score);
4664
4665   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4666   player->LevelSolved_CountingScore = player->score_final;
4667 }
4668
4669 void GameWon()
4670 {
4671   static int time, time_final;
4672   static int score, score_final;
4673   static int game_over_delay_1 = 0;
4674   static int game_over_delay_2 = 0;
4675   int game_over_delay_value_1 = 50;
4676   int game_over_delay_value_2 = 50;
4677
4678   if (!local_player->LevelSolved_GameWon)
4679   {
4680     int i;
4681
4682     /* do not start end game actions before the player stops moving (to exit) */
4683     if (local_player->MovPos)
4684       return;
4685
4686     local_player->LevelSolved_GameWon = TRUE;
4687     local_player->LevelSolved_SaveTape = tape.recording;
4688     local_player->LevelSolved_SaveScore = !tape.playing;
4689
4690     if (tape.auto_play)         /* tape might already be stopped here */
4691       tape.auto_play_level_solved = TRUE;
4692
4693 #if 1
4694     TapeStop();
4695 #endif
4696
4697     game_over_delay_1 = game_over_delay_value_1;
4698     game_over_delay_2 = game_over_delay_value_2;
4699
4700     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4701     score = score_final = local_player->score_final;
4702
4703     if (TimeLeft > 0)
4704     {
4705       time_final = 0;
4706       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4707     }
4708     else if (level.time == 0 && TimePlayed < 999)
4709     {
4710       time_final = 999;
4711       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4712     }
4713
4714     local_player->score_final = score_final;
4715
4716     if (level_editor_test_game)
4717     {
4718       time = time_final;
4719       score = score_final;
4720
4721 #if 1
4722       local_player->LevelSolved_CountingTime = time;
4723       local_player->LevelSolved_CountingScore = score;
4724
4725       game_panel_controls[GAME_PANEL_TIME].value = time;
4726       game_panel_controls[GAME_PANEL_SCORE].value = score;
4727
4728       DisplayGameControlValues();
4729 #else
4730       DrawGameValue_Time(time);
4731       DrawGameValue_Score(score);
4732 #endif
4733     }
4734
4735     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4736     {
4737       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4738       {
4739         /* close exit door after last player */
4740         if ((AllPlayersGone &&
4741              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4742               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4743               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4744             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4745             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4746         {
4747           int element = Feld[ExitX][ExitY];
4748
4749 #if 0
4750           if (element == EL_EM_EXIT_OPEN ||
4751               element == EL_EM_STEEL_EXIT_OPEN)
4752           {
4753             Bang(ExitX, ExitY);
4754           }
4755           else
4756 #endif
4757           {
4758             Feld[ExitX][ExitY] =
4759               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4760                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4761                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4762                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4763                EL_EM_STEEL_EXIT_CLOSING);
4764
4765             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4766           }
4767         }
4768
4769         /* player disappears */
4770         DrawLevelField(ExitX, ExitY);
4771       }
4772
4773       for (i = 0; i < MAX_PLAYERS; i++)
4774       {
4775         struct PlayerInfo *player = &stored_player[i];
4776
4777         if (player->present)
4778         {
4779           RemovePlayer(player);
4780
4781           /* player disappears */
4782           DrawLevelField(player->jx, player->jy);
4783         }
4784       }
4785     }
4786
4787     PlaySound(SND_GAME_WINNING);
4788   }
4789
4790   if (game_over_delay_1 > 0)
4791   {
4792     game_over_delay_1--;
4793
4794     return;
4795   }
4796
4797   if (time != time_final)
4798   {
4799     int time_to_go = ABS(time_final - time);
4800     int time_count_dir = (time < time_final ? +1 : -1);
4801     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4802
4803     time  += time_count_steps * time_count_dir;
4804     score += time_count_steps * level.score[SC_TIME_BONUS];
4805
4806 #if 1
4807     local_player->LevelSolved_CountingTime = time;
4808     local_player->LevelSolved_CountingScore = score;
4809
4810     game_panel_controls[GAME_PANEL_TIME].value = time;
4811     game_panel_controls[GAME_PANEL_SCORE].value = score;
4812
4813     DisplayGameControlValues();
4814 #else
4815     DrawGameValue_Time(time);
4816     DrawGameValue_Score(score);
4817 #endif
4818
4819     if (time == time_final)
4820       StopSound(SND_GAME_LEVELTIME_BONUS);
4821     else if (setup.sound_loops)
4822       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4823     else
4824       PlaySound(SND_GAME_LEVELTIME_BONUS);
4825
4826     return;
4827   }
4828
4829   local_player->LevelSolved_PanelOff = TRUE;
4830
4831   if (game_over_delay_2 > 0)
4832   {
4833     game_over_delay_2--;
4834
4835     return;
4836   }
4837
4838 #if 1
4839   GameEnd();
4840 #endif
4841 }
4842
4843 void GameEnd()
4844 {
4845   int hi_pos;
4846   boolean raise_level = FALSE;
4847
4848   local_player->LevelSolved_GameEnd = TRUE;
4849
4850   CloseDoor(DOOR_CLOSE_1);
4851
4852   if (local_player->LevelSolved_SaveTape)
4853   {
4854 #if 0
4855     TapeStop();
4856 #endif
4857
4858 #if 1
4859     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4860 #else
4861     SaveTape(tape.level_nr);            /* ask to save tape */
4862 #endif
4863   }
4864
4865   if (level_editor_test_game)
4866   {
4867     game_status = GAME_MODE_MAIN;
4868
4869 #if 1
4870     DrawAndFadeInMainMenu(REDRAW_FIELD);
4871 #else
4872     DrawMainMenu();
4873 #endif
4874
4875     return;
4876   }
4877
4878   if (!local_player->LevelSolved_SaveScore)
4879   {
4880 #if 1
4881     FadeOut(REDRAW_FIELD);
4882 #endif
4883
4884     game_status = GAME_MODE_MAIN;
4885
4886     DrawAndFadeInMainMenu(REDRAW_FIELD);
4887
4888     return;
4889   }
4890
4891   if (level_nr == leveldir_current->handicap_level)
4892   {
4893     leveldir_current->handicap_level++;
4894     SaveLevelSetup_SeriesInfo();
4895   }
4896
4897   if (level_nr < leveldir_current->last_level)
4898     raise_level = TRUE;                 /* advance to next level */
4899
4900   if ((hi_pos = NewHiScore()) >= 0) 
4901   {
4902     game_status = GAME_MODE_SCORES;
4903
4904     DrawHallOfFame(hi_pos);
4905
4906     if (raise_level)
4907     {
4908       level_nr++;
4909       TapeErase();
4910     }
4911   }
4912   else
4913   {
4914 #if 1
4915     FadeOut(REDRAW_FIELD);
4916 #endif
4917
4918     game_status = GAME_MODE_MAIN;
4919
4920     if (raise_level)
4921     {
4922       level_nr++;
4923       TapeErase();
4924     }
4925
4926     DrawAndFadeInMainMenu(REDRAW_FIELD);
4927   }
4928 }
4929
4930 int NewHiScore()
4931 {
4932   int k, l;
4933   int position = -1;
4934
4935   LoadScore(level_nr);
4936
4937   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4938       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4939     return -1;
4940
4941   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4942   {
4943     if (local_player->score_final > highscore[k].Score)
4944     {
4945       /* player has made it to the hall of fame */
4946
4947       if (k < MAX_SCORE_ENTRIES - 1)
4948       {
4949         int m = MAX_SCORE_ENTRIES - 1;
4950
4951 #ifdef ONE_PER_NAME
4952         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4953           if (strEqual(setup.player_name, highscore[l].Name))
4954             m = l;
4955         if (m == k)     /* player's new highscore overwrites his old one */
4956           goto put_into_list;
4957 #endif
4958
4959         for (l = m; l > k; l--)
4960         {
4961           strcpy(highscore[l].Name, highscore[l - 1].Name);
4962           highscore[l].Score = highscore[l - 1].Score;
4963         }
4964       }
4965
4966 #ifdef ONE_PER_NAME
4967       put_into_list:
4968 #endif
4969       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4970       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4971       highscore[k].Score = local_player->score_final; 
4972       position = k;
4973       break;
4974     }
4975
4976 #ifdef ONE_PER_NAME
4977     else if (!strncmp(setup.player_name, highscore[k].Name,
4978                       MAX_PLAYER_NAME_LEN))
4979       break;    /* player already there with a higher score */
4980 #endif
4981
4982   }
4983
4984   if (position >= 0) 
4985     SaveScore(level_nr);
4986
4987   return position;
4988 }
4989
4990 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4991 {
4992   int element = Feld[x][y];
4993   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4994   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4995   int horiz_move = (dx != 0);
4996   int sign = (horiz_move ? dx : dy);
4997   int step = sign * element_info[element].move_stepsize;
4998
4999   /* special values for move stepsize for spring and things on conveyor belt */
5000   if (horiz_move)
5001   {
5002     if (CAN_FALL(element) &&
5003         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5004       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5005     else if (element == EL_SPRING)
5006       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5007   }
5008
5009   return step;
5010 }
5011
5012 inline static int getElementMoveStepsize(int x, int y)
5013 {
5014   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5015 }
5016
5017 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5018 {
5019   if (player->GfxAction != action || player->GfxDir != dir)
5020   {
5021 #if 0
5022     printf("Player frame reset! (%d => %d, %d => %d)\n",
5023            player->GfxAction, action, player->GfxDir, dir);
5024 #endif
5025
5026     player->GfxAction = action;
5027     player->GfxDir = dir;
5028     player->Frame = 0;
5029     player->StepFrame = 0;
5030   }
5031 }
5032
5033 #if USE_GFX_RESET_GFX_ANIMATION
5034 static void ResetGfxFrame(int x, int y, boolean redraw)
5035 {
5036   int element = Feld[x][y];
5037   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5038   int last_gfx_frame = GfxFrame[x][y];
5039
5040   if (graphic_info[graphic].anim_global_sync)
5041     GfxFrame[x][y] = FrameCounter;
5042   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5043     GfxFrame[x][y] = CustomValue[x][y];
5044   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5045     GfxFrame[x][y] = element_info[element].collect_score;
5046   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5047     GfxFrame[x][y] = ChangeDelay[x][y];
5048
5049   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5050     DrawLevelGraphicAnimation(x, y, graphic);
5051 }
5052 #endif
5053
5054 static void ResetGfxAnimation(int x, int y)
5055 {
5056   GfxAction[x][y] = ACTION_DEFAULT;
5057   GfxDir[x][y] = MovDir[x][y];
5058   GfxFrame[x][y] = 0;
5059
5060 #if USE_GFX_RESET_GFX_ANIMATION
5061   ResetGfxFrame(x, y, FALSE);
5062 #endif
5063 }
5064
5065 static void ResetRandomAnimationValue(int x, int y)
5066 {
5067   GfxRandom[x][y] = INIT_GFX_RANDOM();
5068 }
5069
5070 void InitMovingField(int x, int y, int direction)
5071 {
5072   int element = Feld[x][y];
5073   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5074   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5075   int newx = x + dx;
5076   int newy = y + dy;
5077   boolean is_moving_before, is_moving_after;
5078 #if 0
5079   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5080 #endif
5081
5082   /* check if element was/is moving or being moved before/after mode change */
5083 #if 1
5084 #if 1
5085   is_moving_before = (WasJustMoving[x][y] != 0);
5086 #else
5087   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5088   is_moving_before = WasJustMoving[x][y];
5089 #endif
5090 #else
5091   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5092 #endif
5093   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5094
5095   /* reset animation only for moving elements which change direction of moving
5096      or which just started or stopped moving
5097      (else CEs with property "can move" / "not moving" are reset each frame) */
5098 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5099 #if 1
5100   if (is_moving_before != is_moving_after ||
5101       direction != MovDir[x][y])
5102     ResetGfxAnimation(x, y);
5103 #else
5104   if ((is_moving_before || is_moving_after) && !continues_moving)
5105     ResetGfxAnimation(x, y);
5106 #endif
5107 #else
5108   if (!continues_moving)
5109     ResetGfxAnimation(x, y);
5110 #endif
5111
5112   MovDir[x][y] = direction;
5113   GfxDir[x][y] = direction;
5114
5115 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5116   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5117                      direction == MV_DOWN && CAN_FALL(element) ?
5118                      ACTION_FALLING : ACTION_MOVING);
5119 #else
5120   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5121                      ACTION_FALLING : ACTION_MOVING);
5122 #endif
5123
5124   /* this is needed for CEs with property "can move" / "not moving" */
5125
5126   if (is_moving_after)
5127   {
5128     if (Feld[newx][newy] == EL_EMPTY)
5129       Feld[newx][newy] = EL_BLOCKED;
5130
5131     MovDir[newx][newy] = MovDir[x][y];
5132
5133 #if USE_NEW_CUSTOM_VALUE
5134     CustomValue[newx][newy] = CustomValue[x][y];
5135 #endif
5136
5137     GfxFrame[newx][newy] = GfxFrame[x][y];
5138     GfxRandom[newx][newy] = GfxRandom[x][y];
5139     GfxAction[newx][newy] = GfxAction[x][y];
5140     GfxDir[newx][newy] = GfxDir[x][y];
5141   }
5142 }
5143
5144 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5145 {
5146   int direction = MovDir[x][y];
5147   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5148   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5149
5150   *goes_to_x = newx;
5151   *goes_to_y = newy;
5152 }
5153
5154 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5155 {
5156   int oldx = x, oldy = y;
5157   int direction = MovDir[x][y];
5158
5159   if (direction == MV_LEFT)
5160     oldx++;
5161   else if (direction == MV_RIGHT)
5162     oldx--;
5163   else if (direction == MV_UP)
5164     oldy++;
5165   else if (direction == MV_DOWN)
5166     oldy--;
5167
5168   *comes_from_x = oldx;
5169   *comes_from_y = oldy;
5170 }
5171
5172 int MovingOrBlocked2Element(int x, int y)
5173 {
5174   int element = Feld[x][y];
5175
5176   if (element == EL_BLOCKED)
5177   {
5178     int oldx, oldy;
5179
5180     Blocked2Moving(x, y, &oldx, &oldy);
5181     return Feld[oldx][oldy];
5182   }
5183   else
5184     return element;
5185 }
5186
5187 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5188 {
5189   /* like MovingOrBlocked2Element(), but if element is moving
5190      and (x,y) is the field the moving element is just leaving,
5191      return EL_BLOCKED instead of the element value */
5192   int element = Feld[x][y];
5193
5194   if (IS_MOVING(x, y))
5195   {
5196     if (element == EL_BLOCKED)
5197     {
5198       int oldx, oldy;
5199
5200       Blocked2Moving(x, y, &oldx, &oldy);
5201       return Feld[oldx][oldy];
5202     }
5203     else
5204       return EL_BLOCKED;
5205   }
5206   else
5207     return element;
5208 }
5209
5210 static void RemoveField(int x, int y)
5211 {
5212   Feld[x][y] = EL_EMPTY;
5213
5214   MovPos[x][y] = 0;
5215   MovDir[x][y] = 0;
5216   MovDelay[x][y] = 0;
5217
5218 #if USE_NEW_CUSTOM_VALUE
5219   CustomValue[x][y] = 0;
5220 #endif
5221
5222   AmoebaNr[x][y] = 0;
5223   ChangeDelay[x][y] = 0;
5224   ChangePage[x][y] = -1;
5225   Pushed[x][y] = FALSE;
5226
5227 #if 0
5228   ExplodeField[x][y] = EX_TYPE_NONE;
5229 #endif
5230
5231   GfxElement[x][y] = EL_UNDEFINED;
5232   GfxAction[x][y] = ACTION_DEFAULT;
5233   GfxDir[x][y] = MV_NONE;
5234 #if 0
5235   /* !!! this would prevent the removed tile from being redrawn !!! */
5236   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5237 #endif
5238 }
5239
5240 void RemoveMovingField(int x, int y)
5241 {
5242   int oldx = x, oldy = y, newx = x, newy = y;
5243   int element = Feld[x][y];
5244   int next_element = EL_UNDEFINED;
5245
5246   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5247     return;
5248
5249   if (IS_MOVING(x, y))
5250   {
5251     Moving2Blocked(x, y, &newx, &newy);
5252
5253     if (Feld[newx][newy] != EL_BLOCKED)
5254     {
5255       /* element is moving, but target field is not free (blocked), but
5256          already occupied by something different (example: acid pool);
5257          in this case, only remove the moving field, but not the target */
5258
5259       RemoveField(oldx, oldy);
5260
5261       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5262
5263       TEST_DrawLevelField(oldx, oldy);
5264
5265       return;
5266     }
5267   }
5268   else if (element == EL_BLOCKED)
5269   {
5270     Blocked2Moving(x, y, &oldx, &oldy);
5271     if (!IS_MOVING(oldx, oldy))
5272       return;
5273   }
5274
5275   if (element == EL_BLOCKED &&
5276       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5277        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5278        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5279        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5280        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5281        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5282     next_element = get_next_element(Feld[oldx][oldy]);
5283
5284   RemoveField(oldx, oldy);
5285   RemoveField(newx, newy);
5286
5287   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5288
5289   if (next_element != EL_UNDEFINED)
5290     Feld[oldx][oldy] = next_element;
5291
5292   TEST_DrawLevelField(oldx, oldy);
5293   TEST_DrawLevelField(newx, newy);
5294 }
5295
5296 void DrawDynamite(int x, int y)
5297 {
5298   int sx = SCREENX(x), sy = SCREENY(y);
5299   int graphic = el2img(Feld[x][y]);
5300   int frame;
5301
5302   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5303     return;
5304
5305   if (IS_WALKABLE_INSIDE(Back[x][y]))
5306     return;
5307
5308   if (Back[x][y])
5309     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5310   else if (Store[x][y])
5311     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5312
5313   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5314
5315   if (Back[x][y] || Store[x][y])
5316     DrawGraphicThruMask(sx, sy, graphic, frame);
5317   else
5318     DrawGraphic(sx, sy, graphic, frame);
5319 }
5320
5321 void CheckDynamite(int x, int y)
5322 {
5323   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5324   {
5325     MovDelay[x][y]--;
5326
5327     if (MovDelay[x][y] != 0)
5328     {
5329       DrawDynamite(x, y);
5330       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5331
5332       return;
5333     }
5334   }
5335
5336   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5337
5338   Bang(x, y);
5339 }
5340
5341 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5342 {
5343   boolean num_checked_players = 0;
5344   int i;
5345
5346   for (i = 0; i < MAX_PLAYERS; i++)
5347   {
5348     if (stored_player[i].active)
5349     {
5350       int sx = stored_player[i].jx;
5351       int sy = stored_player[i].jy;
5352
5353       if (num_checked_players == 0)
5354       {
5355         *sx1 = *sx2 = sx;
5356         *sy1 = *sy2 = sy;
5357       }
5358       else
5359       {
5360         *sx1 = MIN(*sx1, sx);
5361         *sy1 = MIN(*sy1, sy);
5362         *sx2 = MAX(*sx2, sx);
5363         *sy2 = MAX(*sy2, sy);
5364       }
5365
5366       num_checked_players++;
5367     }
5368   }
5369 }
5370
5371 static boolean checkIfAllPlayersFitToScreen_RND()
5372 {
5373   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5374
5375   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5376
5377   return (sx2 - sx1 < SCR_FIELDX &&
5378           sy2 - sy1 < SCR_FIELDY);
5379 }
5380
5381 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5382 {
5383   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5384
5385   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5386
5387   *sx = (sx1 + sx2) / 2;
5388   *sy = (sy1 + sy2) / 2;
5389 }
5390
5391 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5392                         boolean center_screen, boolean quick_relocation)
5393 {
5394   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5395   boolean no_delay = (tape.warp_forward);
5396   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5397   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5398
5399   if (quick_relocation)
5400   {
5401     int offset = game.scroll_delay_value;
5402
5403     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5404     {
5405       if (!level.shifted_relocation || center_screen)
5406       {
5407         /* quick relocation (without scrolling), with centering of screen */
5408
5409         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5410                     x > SBX_Right + MIDPOSX ? SBX_Right :
5411                     x - MIDPOSX);
5412
5413         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5414                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5415                     y - MIDPOSY);
5416       }
5417       else
5418       {
5419         /* quick relocation (without scrolling), but do not center screen */
5420
5421         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5422                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5423                                old_x - MIDPOSX);
5424
5425         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5426                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5427                                old_y - MIDPOSY);
5428
5429         int offset_x = x + (scroll_x - center_scroll_x);
5430         int offset_y = y + (scroll_y - center_scroll_y);
5431
5432         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5433                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5434                     offset_x - MIDPOSX);
5435
5436         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5437                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5438                     offset_y - MIDPOSY);
5439       }
5440     }
5441     else
5442     {
5443       /* quick relocation (without scrolling), inside visible screen area */
5444
5445       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5446           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5447         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5448
5449       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5450           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5451         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5452
5453       /* don't scroll over playfield boundaries */
5454       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5455         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5456
5457       /* don't scroll over playfield boundaries */
5458       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5459         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5460     }
5461
5462     RedrawPlayfield(TRUE, 0,0,0,0);
5463   }
5464   else
5465   {
5466 #if 1
5467     int scroll_xx, scroll_yy;
5468
5469     if (!level.shifted_relocation || center_screen)
5470     {
5471       /* visible relocation (with scrolling), with centering of screen */
5472
5473       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5474                    x > SBX_Right + MIDPOSX ? SBX_Right :
5475                    x - MIDPOSX);
5476
5477       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5478                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5479                    y - MIDPOSY);
5480     }
5481     else
5482     {
5483       /* visible relocation (with scrolling), but do not center screen */
5484
5485       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5486                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5487                              old_x - MIDPOSX);
5488
5489       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5490                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5491                              old_y - MIDPOSY);
5492
5493       int offset_x = x + (scroll_x - center_scroll_x);
5494       int offset_y = y + (scroll_y - center_scroll_y);
5495
5496       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5497                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5498                    offset_x - MIDPOSX);
5499
5500       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5501                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5502                    offset_y - MIDPOSY);
5503     }
5504
5505 #else
5506
5507     /* visible relocation (with scrolling), with centering of screen */
5508
5509     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5510                      x > SBX_Right + MIDPOSX ? SBX_Right :
5511                      x - MIDPOSX);
5512
5513     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5514                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5515                      y - MIDPOSY);
5516 #endif
5517
5518     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5519
5520     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5521     {
5522       int dx = 0, dy = 0;
5523       int fx = FX, fy = FY;
5524
5525       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5526       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5527
5528       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5529         break;
5530
5531       scroll_x -= dx;
5532       scroll_y -= dy;
5533
5534       fx += dx * TILEX / 2;
5535       fy += dy * TILEY / 2;
5536
5537       ScrollLevel(dx, dy);
5538       DrawAllPlayers();
5539
5540       /* scroll in two steps of half tile size to make things smoother */
5541       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5542       FlushDisplay();
5543       Delay(wait_delay_value);
5544
5545       /* scroll second step to align at full tile size */
5546       BackToFront();
5547       Delay(wait_delay_value);
5548     }
5549
5550     DrawAllPlayers();
5551     BackToFront();
5552     Delay(wait_delay_value);
5553   }
5554 }
5555
5556 void RelocatePlayer(int jx, int jy, int el_player_raw)
5557 {
5558   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5559   int player_nr = GET_PLAYER_NR(el_player);
5560   struct PlayerInfo *player = &stored_player[player_nr];
5561   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5562   boolean no_delay = (tape.warp_forward);
5563   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5564   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5565   int old_jx = player->jx;
5566   int old_jy = player->jy;
5567   int old_element = Feld[old_jx][old_jy];
5568   int element = Feld[jx][jy];
5569   boolean player_relocated = (old_jx != jx || old_jy != jy);
5570
5571   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5572   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5573   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5574   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5575   int leave_side_horiz = move_dir_horiz;
5576   int leave_side_vert  = move_dir_vert;
5577   int enter_side = enter_side_horiz | enter_side_vert;
5578   int leave_side = leave_side_horiz | leave_side_vert;
5579
5580   if (player->GameOver)         /* do not reanimate dead player */
5581     return;
5582
5583   if (!player_relocated)        /* no need to relocate the player */
5584     return;
5585
5586   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5587   {
5588     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5589     DrawLevelField(jx, jy);
5590   }
5591
5592   if (player->present)
5593   {
5594     while (player->MovPos)
5595     {
5596       ScrollPlayer(player, SCROLL_GO_ON);
5597       ScrollScreen(NULL, SCROLL_GO_ON);
5598
5599       AdvanceFrameAndPlayerCounters(player->index_nr);
5600
5601       DrawPlayer(player);
5602
5603       BackToFront();
5604       Delay(wait_delay_value);
5605     }
5606
5607     DrawPlayer(player);         /* needed here only to cleanup last field */
5608     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5609
5610     player->is_moving = FALSE;
5611   }
5612
5613   if (IS_CUSTOM_ELEMENT(old_element))
5614     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5615                                CE_LEFT_BY_PLAYER,
5616                                player->index_bit, leave_side);
5617
5618   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5619                                       CE_PLAYER_LEAVES_X,
5620                                       player->index_bit, leave_side);
5621
5622   Feld[jx][jy] = el_player;
5623   InitPlayerField(jx, jy, el_player, TRUE);
5624
5625   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5626   {
5627     Feld[jx][jy] = element;
5628     InitField(jx, jy, FALSE);
5629   }
5630
5631   /* only visually relocate centered player */
5632   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5633                      FALSE, level.instant_relocation);
5634
5635   TestIfPlayerTouchesBadThing(jx, jy);
5636   TestIfPlayerTouchesCustomElement(jx, jy);
5637
5638   if (IS_CUSTOM_ELEMENT(element))
5639     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5640                                player->index_bit, enter_side);
5641
5642   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5643                                       player->index_bit, enter_side);
5644
5645 #if 1
5646   if (player->is_switching)
5647   {
5648     /* ensure that relocation while still switching an element does not cause
5649        a new element to be treated as also switched directly after relocation
5650        (this is important for teleporter switches that teleport the player to
5651        a place where another teleporter switch is in the same direction, which
5652        would then incorrectly be treated as immediately switched before the
5653        direction key that caused the switch was released) */
5654
5655     player->switch_x += jx - old_jx;
5656     player->switch_y += jy - old_jy;
5657   }
5658 #endif
5659 }
5660
5661 void Explode(int ex, int ey, int phase, int mode)
5662 {
5663   int x, y;
5664   int last_phase;
5665   int border_element;
5666
5667   /* !!! eliminate this variable !!! */
5668   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5669
5670   if (game.explosions_delayed)
5671   {
5672     ExplodeField[ex][ey] = mode;
5673     return;
5674   }
5675
5676   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5677   {
5678     int center_element = Feld[ex][ey];
5679     int artwork_element, explosion_element;     /* set these values later */
5680
5681 #if 0
5682     /* --- This is only really needed (and now handled) in "Impact()". --- */
5683     /* do not explode moving elements that left the explode field in time */
5684     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5685         center_element == EL_EMPTY &&
5686         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5687       return;
5688 #endif
5689
5690 #if 0
5691     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5692     if (mode == EX_TYPE_NORMAL ||
5693         mode == EX_TYPE_CENTER ||
5694         mode == EX_TYPE_CROSS)
5695       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5696 #endif
5697
5698     /* remove things displayed in background while burning dynamite */
5699     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5700       Back[ex][ey] = 0;
5701
5702     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5703     {
5704       /* put moving element to center field (and let it explode there) */
5705       center_element = MovingOrBlocked2Element(ex, ey);
5706       RemoveMovingField(ex, ey);
5707       Feld[ex][ey] = center_element;
5708     }
5709
5710     /* now "center_element" is finally determined -- set related values now */
5711     artwork_element = center_element;           /* for custom player artwork */
5712     explosion_element = center_element;         /* for custom player artwork */
5713
5714     if (IS_PLAYER(ex, ey))
5715     {
5716       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5717
5718       artwork_element = stored_player[player_nr].artwork_element;
5719
5720       if (level.use_explosion_element[player_nr])
5721       {
5722         explosion_element = level.explosion_element[player_nr];
5723         artwork_element = explosion_element;
5724       }
5725     }
5726
5727 #if 1
5728     if (mode == EX_TYPE_NORMAL ||
5729         mode == EX_TYPE_CENTER ||
5730         mode == EX_TYPE_CROSS)
5731       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5732 #endif
5733
5734     last_phase = element_info[explosion_element].explosion_delay + 1;
5735
5736     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5737     {
5738       int xx = x - ex + 1;
5739       int yy = y - ey + 1;
5740       int element;
5741
5742       if (!IN_LEV_FIELD(x, y) ||
5743           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5744           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5745         continue;
5746
5747       element = Feld[x][y];
5748
5749       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5750       {
5751         element = MovingOrBlocked2Element(x, y);
5752
5753         if (!IS_EXPLOSION_PROOF(element))
5754           RemoveMovingField(x, y);
5755       }
5756
5757       /* indestructible elements can only explode in center (but not flames) */
5758       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5759                                            mode == EX_TYPE_BORDER)) ||
5760           element == EL_FLAMES)
5761         continue;
5762
5763       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5764          behaviour, for example when touching a yamyam that explodes to rocks
5765          with active deadly shield, a rock is created under the player !!! */
5766       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5767 #if 0
5768       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5769           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5770            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5771 #else
5772       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5773 #endif
5774       {
5775         if (IS_ACTIVE_BOMB(element))
5776         {
5777           /* re-activate things under the bomb like gate or penguin */
5778           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5779           Back[x][y] = 0;
5780         }
5781
5782         continue;
5783       }
5784
5785       /* save walkable background elements while explosion on same tile */
5786       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5787           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5788         Back[x][y] = element;
5789
5790       /* ignite explodable elements reached by other explosion */
5791       if (element == EL_EXPLOSION)
5792         element = Store2[x][y];
5793
5794       if (AmoebaNr[x][y] &&
5795           (element == EL_AMOEBA_FULL ||
5796            element == EL_BD_AMOEBA ||
5797            element == EL_AMOEBA_GROWING))
5798       {
5799         AmoebaCnt[AmoebaNr[x][y]]--;
5800         AmoebaCnt2[AmoebaNr[x][y]]--;
5801       }
5802
5803       RemoveField(x, y);
5804
5805       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5806       {
5807         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5808
5809         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5810
5811         if (PLAYERINFO(ex, ey)->use_murphy)
5812           Store[x][y] = EL_EMPTY;
5813       }
5814
5815       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5816          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5817       else if (ELEM_IS_PLAYER(center_element))
5818         Store[x][y] = EL_EMPTY;
5819       else if (center_element == EL_YAMYAM)
5820         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5821       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5822         Store[x][y] = element_info[center_element].content.e[xx][yy];
5823 #if 1
5824       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5825          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5826          otherwise) -- FIX THIS !!! */
5827       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5828         Store[x][y] = element_info[element].content.e[1][1];
5829 #else
5830       else if (!CAN_EXPLODE(element))
5831         Store[x][y] = element_info[element].content.e[1][1];
5832 #endif
5833       else
5834         Store[x][y] = EL_EMPTY;
5835
5836       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5837           center_element == EL_AMOEBA_TO_DIAMOND)
5838         Store2[x][y] = element;
5839
5840       Feld[x][y] = EL_EXPLOSION;
5841       GfxElement[x][y] = artwork_element;
5842
5843       ExplodePhase[x][y] = 1;
5844       ExplodeDelay[x][y] = last_phase;
5845
5846       Stop[x][y] = TRUE;
5847     }
5848
5849     if (center_element == EL_YAMYAM)
5850       game.yamyam_content_nr =
5851         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5852
5853     return;
5854   }
5855
5856   if (Stop[ex][ey])
5857     return;
5858
5859   x = ex;
5860   y = ey;
5861
5862   if (phase == 1)
5863     GfxFrame[x][y] = 0;         /* restart explosion animation */
5864
5865   last_phase = ExplodeDelay[x][y];
5866
5867   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5868
5869 #ifdef DEBUG
5870
5871   /* activate this even in non-DEBUG version until cause for crash in
5872      getGraphicAnimationFrame() (see below) is found and eliminated */
5873
5874 #endif
5875 #if 1
5876
5877 #if 1
5878   /* this can happen if the player leaves an explosion just in time */
5879   if (GfxElement[x][y] == EL_UNDEFINED)
5880     GfxElement[x][y] = EL_EMPTY;
5881 #else
5882   if (GfxElement[x][y] == EL_UNDEFINED)
5883   {
5884     printf("\n\n");
5885     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5886     printf("Explode(): This should never happen!\n");
5887     printf("\n\n");
5888
5889     GfxElement[x][y] = EL_EMPTY;
5890   }
5891 #endif
5892
5893 #endif
5894
5895   border_element = Store2[x][y];
5896   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5897     border_element = StorePlayer[x][y];
5898
5899   if (phase == element_info[border_element].ignition_delay ||
5900       phase == last_phase)
5901   {
5902     boolean border_explosion = FALSE;
5903
5904     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5905         !PLAYER_EXPLOSION_PROTECTED(x, y))
5906     {
5907       KillPlayerUnlessExplosionProtected(x, y);
5908       border_explosion = TRUE;
5909     }
5910     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5911     {
5912       Feld[x][y] = Store2[x][y];
5913       Store2[x][y] = 0;
5914       Bang(x, y);
5915       border_explosion = TRUE;
5916     }
5917     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5918     {
5919       AmoebeUmwandeln(x, y);
5920       Store2[x][y] = 0;
5921       border_explosion = TRUE;
5922     }
5923
5924     /* if an element just explodes due to another explosion (chain-reaction),
5925        do not immediately end the new explosion when it was the last frame of
5926        the explosion (as it would be done in the following "if"-statement!) */
5927     if (border_explosion && phase == last_phase)
5928       return;
5929   }
5930
5931   if (phase == last_phase)
5932   {
5933     int element;
5934
5935     element = Feld[x][y] = Store[x][y];
5936     Store[x][y] = Store2[x][y] = 0;
5937     GfxElement[x][y] = EL_UNDEFINED;
5938
5939     /* player can escape from explosions and might therefore be still alive */
5940     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5941         element <= EL_PLAYER_IS_EXPLODING_4)
5942     {
5943       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5944       int explosion_element = EL_PLAYER_1 + player_nr;
5945       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5946       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5947
5948       if (level.use_explosion_element[player_nr])
5949         explosion_element = level.explosion_element[player_nr];
5950
5951       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5952                     element_info[explosion_element].content.e[xx][yy]);
5953     }
5954
5955     /* restore probably existing indestructible background element */
5956     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5957       element = Feld[x][y] = Back[x][y];
5958     Back[x][y] = 0;
5959
5960     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5961     GfxDir[x][y] = MV_NONE;
5962     ChangeDelay[x][y] = 0;
5963     ChangePage[x][y] = -1;
5964
5965 #if USE_NEW_CUSTOM_VALUE
5966     CustomValue[x][y] = 0;
5967 #endif
5968
5969     InitField_WithBug2(x, y, FALSE);
5970
5971     TEST_DrawLevelField(x, y);
5972
5973     TestIfElementTouchesCustomElement(x, y);
5974
5975     if (GFX_CRUMBLED(element))
5976       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5977
5978     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5979       StorePlayer[x][y] = 0;
5980
5981     if (ELEM_IS_PLAYER(element))
5982       RelocatePlayer(x, y, element);
5983   }
5984   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5985   {
5986     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5987     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5988
5989     if (phase == delay)
5990       TEST_DrawLevelFieldCrumbledSand(x, y);
5991
5992     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5993     {
5994       DrawLevelElement(x, y, Back[x][y]);
5995       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5996     }
5997     else if (IS_WALKABLE_UNDER(Back[x][y]))
5998     {
5999       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6000       DrawLevelElementThruMask(x, y, Back[x][y]);
6001     }
6002     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6003       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6004   }
6005 }
6006
6007 void DynaExplode(int ex, int ey)
6008 {
6009   int i, j;
6010   int dynabomb_element = Feld[ex][ey];
6011   int dynabomb_size = 1;
6012   boolean dynabomb_xl = FALSE;
6013   struct PlayerInfo *player;
6014   static int xy[4][2] =
6015   {
6016     { 0, -1 },
6017     { -1, 0 },
6018     { +1, 0 },
6019     { 0, +1 }
6020   };
6021
6022   if (IS_ACTIVE_BOMB(dynabomb_element))
6023   {
6024     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6025     dynabomb_size = player->dynabomb_size;
6026     dynabomb_xl = player->dynabomb_xl;
6027     player->dynabombs_left++;
6028   }
6029
6030   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6031
6032   for (i = 0; i < NUM_DIRECTIONS; i++)
6033   {
6034     for (j = 1; j <= dynabomb_size; j++)
6035     {
6036       int x = ex + j * xy[i][0];
6037       int y = ey + j * xy[i][1];
6038       int element;
6039
6040       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6041         break;
6042
6043       element = Feld[x][y];
6044
6045       /* do not restart explosions of fields with active bombs */
6046       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6047         continue;
6048
6049       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6050
6051       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6052           !IS_DIGGABLE(element) && !dynabomb_xl)
6053         break;
6054     }
6055   }
6056 }
6057
6058 void Bang(int x, int y)
6059 {
6060   int element = MovingOrBlocked2Element(x, y);
6061   int explosion_type = EX_TYPE_NORMAL;
6062
6063   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6064   {
6065     struct PlayerInfo *player = PLAYERINFO(x, y);
6066
6067 #if USE_FIX_CE_ACTION_WITH_PLAYER
6068     element = Feld[x][y] = player->initial_element;
6069 #else
6070     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6071                             player->element_nr);
6072 #endif
6073
6074     if (level.use_explosion_element[player->index_nr])
6075     {
6076       int explosion_element = level.explosion_element[player->index_nr];
6077
6078       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6079         explosion_type = EX_TYPE_CROSS;
6080       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6081         explosion_type = EX_TYPE_CENTER;
6082     }
6083   }
6084
6085   switch (element)
6086   {
6087     case EL_BUG:
6088     case EL_SPACESHIP:
6089     case EL_BD_BUTTERFLY:
6090     case EL_BD_FIREFLY:
6091     case EL_YAMYAM:
6092     case EL_DARK_YAMYAM:
6093     case EL_ROBOT:
6094     case EL_PACMAN:
6095     case EL_MOLE:
6096       RaiseScoreElement(element);
6097       break;
6098
6099     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6100     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6101     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6102     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6103     case EL_DYNABOMB_INCREASE_NUMBER:
6104     case EL_DYNABOMB_INCREASE_SIZE:
6105     case EL_DYNABOMB_INCREASE_POWER:
6106       explosion_type = EX_TYPE_DYNA;
6107       break;
6108
6109     case EL_DC_LANDMINE:
6110 #if 0
6111     case EL_EM_EXIT_OPEN:
6112     case EL_EM_STEEL_EXIT_OPEN:
6113 #endif
6114       explosion_type = EX_TYPE_CENTER;
6115       break;
6116
6117     case EL_PENGUIN:
6118     case EL_LAMP:
6119     case EL_LAMP_ACTIVE:
6120     case EL_AMOEBA_TO_DIAMOND:
6121       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6122         explosion_type = EX_TYPE_CENTER;
6123       break;
6124
6125     default:
6126       if (element_info[element].explosion_type == EXPLODES_CROSS)
6127         explosion_type = EX_TYPE_CROSS;
6128       else if (element_info[element].explosion_type == EXPLODES_1X1)
6129         explosion_type = EX_TYPE_CENTER;
6130       break;
6131   }
6132
6133   if (explosion_type == EX_TYPE_DYNA)
6134     DynaExplode(x, y);
6135   else
6136     Explode(x, y, EX_PHASE_START, explosion_type);
6137
6138   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6139 }
6140
6141 void SplashAcid(int x, int y)
6142 {
6143   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6144       (!IN_LEV_FIELD(x - 1, y - 2) ||
6145        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6146     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6147
6148   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6149       (!IN_LEV_FIELD(x + 1, y - 2) ||
6150        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6151     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6152
6153   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6154 }
6155
6156 static void InitBeltMovement()
6157 {
6158   static int belt_base_element[4] =
6159   {
6160     EL_CONVEYOR_BELT_1_LEFT,
6161     EL_CONVEYOR_BELT_2_LEFT,
6162     EL_CONVEYOR_BELT_3_LEFT,
6163     EL_CONVEYOR_BELT_4_LEFT
6164   };
6165   static int belt_base_active_element[4] =
6166   {
6167     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6170     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6171   };
6172
6173   int x, y, i, j;
6174
6175   /* set frame order for belt animation graphic according to belt direction */
6176   for (i = 0; i < NUM_BELTS; i++)
6177   {
6178     int belt_nr = i;
6179
6180     for (j = 0; j < NUM_BELT_PARTS; j++)
6181     {
6182       int element = belt_base_active_element[belt_nr] + j;
6183       int graphic_1 = el2img(element);
6184       int graphic_2 = el2panelimg(element);
6185
6186       if (game.belt_dir[i] == MV_LEFT)
6187       {
6188         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6189         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6190       }
6191       else
6192       {
6193         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6194         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6195       }
6196     }
6197   }
6198
6199   SCAN_PLAYFIELD(x, y)
6200   {
6201     int element = Feld[x][y];
6202
6203     for (i = 0; i < NUM_BELTS; i++)
6204     {
6205       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6206       {
6207         int e_belt_nr = getBeltNrFromBeltElement(element);
6208         int belt_nr = i;
6209
6210         if (e_belt_nr == belt_nr)
6211         {
6212           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6213
6214           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6215         }
6216       }
6217     }
6218   }
6219 }
6220
6221 static void ToggleBeltSwitch(int x, int y)
6222 {
6223   static int belt_base_element[4] =
6224   {
6225     EL_CONVEYOR_BELT_1_LEFT,
6226     EL_CONVEYOR_BELT_2_LEFT,
6227     EL_CONVEYOR_BELT_3_LEFT,
6228     EL_CONVEYOR_BELT_4_LEFT
6229   };
6230   static int belt_base_active_element[4] =
6231   {
6232     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6233     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6234     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6235     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6236   };
6237   static int belt_base_switch_element[4] =
6238   {
6239     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6240     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6241     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6242     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6243   };
6244   static int belt_move_dir[4] =
6245   {
6246     MV_LEFT,
6247     MV_NONE,
6248     MV_RIGHT,
6249     MV_NONE,
6250   };
6251
6252   int element = Feld[x][y];
6253   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6254   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6255   int belt_dir = belt_move_dir[belt_dir_nr];
6256   int xx, yy, i;
6257
6258   if (!IS_BELT_SWITCH(element))
6259     return;
6260
6261   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6262   game.belt_dir[belt_nr] = belt_dir;
6263
6264   if (belt_dir_nr == 3)
6265     belt_dir_nr = 1;
6266
6267   /* set frame order for belt animation graphic according to belt direction */
6268   for (i = 0; i < NUM_BELT_PARTS; i++)
6269   {
6270     int element = belt_base_active_element[belt_nr] + i;
6271     int graphic_1 = el2img(element);
6272     int graphic_2 = el2panelimg(element);
6273
6274     if (belt_dir == MV_LEFT)
6275     {
6276       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6277       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6278     }
6279     else
6280     {
6281       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6282       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6283     }
6284   }
6285
6286   SCAN_PLAYFIELD(xx, yy)
6287   {
6288     int element = Feld[xx][yy];
6289
6290     if (IS_BELT_SWITCH(element))
6291     {
6292       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6293
6294       if (e_belt_nr == belt_nr)
6295       {
6296         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6297         TEST_DrawLevelField(xx, yy);
6298       }
6299     }
6300     else if (IS_BELT(element) && belt_dir != MV_NONE)
6301     {
6302       int e_belt_nr = getBeltNrFromBeltElement(element);
6303
6304       if (e_belt_nr == belt_nr)
6305       {
6306         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6307
6308         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6309         TEST_DrawLevelField(xx, yy);
6310       }
6311     }
6312     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6313     {
6314       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6315
6316       if (e_belt_nr == belt_nr)
6317       {
6318         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6319
6320         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6321         TEST_DrawLevelField(xx, yy);
6322       }
6323     }
6324   }
6325 }
6326
6327 static void ToggleSwitchgateSwitch(int x, int y)
6328 {
6329   int xx, yy;
6330
6331   game.switchgate_pos = !game.switchgate_pos;
6332
6333   SCAN_PLAYFIELD(xx, yy)
6334   {
6335     int element = Feld[xx][yy];
6336
6337 #if !USE_BOTH_SWITCHGATE_SWITCHES
6338     if (element == EL_SWITCHGATE_SWITCH_UP ||
6339         element == EL_SWITCHGATE_SWITCH_DOWN)
6340     {
6341       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6342       TEST_DrawLevelField(xx, yy);
6343     }
6344     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6345              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6346     {
6347       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6348       TEST_DrawLevelField(xx, yy);
6349     }
6350 #else
6351     if (element == EL_SWITCHGATE_SWITCH_UP)
6352     {
6353       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6354       TEST_DrawLevelField(xx, yy);
6355     }
6356     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6357     {
6358       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6359       TEST_DrawLevelField(xx, yy);
6360     }
6361     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6362     {
6363       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6364       TEST_DrawLevelField(xx, yy);
6365     }
6366     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6367     {
6368       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6369       TEST_DrawLevelField(xx, yy);
6370     }
6371 #endif
6372     else if (element == EL_SWITCHGATE_OPEN ||
6373              element == EL_SWITCHGATE_OPENING)
6374     {
6375       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6376
6377       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6378     }
6379     else if (element == EL_SWITCHGATE_CLOSED ||
6380              element == EL_SWITCHGATE_CLOSING)
6381     {
6382       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6383
6384       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6385     }
6386   }
6387 }
6388
6389 static int getInvisibleActiveFromInvisibleElement(int element)
6390 {
6391   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6392           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6393           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6394           element);
6395 }
6396
6397 static int getInvisibleFromInvisibleActiveElement(int element)
6398 {
6399   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6400           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6401           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6402           element);
6403 }
6404
6405 static void RedrawAllLightSwitchesAndInvisibleElements()
6406 {
6407   int x, y;
6408
6409   SCAN_PLAYFIELD(x, y)
6410   {
6411     int element = Feld[x][y];
6412
6413     if (element == EL_LIGHT_SWITCH &&
6414         game.light_time_left > 0)
6415     {
6416       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6417       TEST_DrawLevelField(x, y);
6418     }
6419     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6420              game.light_time_left == 0)
6421     {
6422       Feld[x][y] = EL_LIGHT_SWITCH;
6423       TEST_DrawLevelField(x, y);
6424     }
6425     else if (element == EL_EMC_DRIPPER &&
6426              game.light_time_left > 0)
6427     {
6428       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6429       TEST_DrawLevelField(x, y);
6430     }
6431     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6432              game.light_time_left == 0)
6433     {
6434       Feld[x][y] = EL_EMC_DRIPPER;
6435       TEST_DrawLevelField(x, y);
6436     }
6437     else if (element == EL_INVISIBLE_STEELWALL ||
6438              element == EL_INVISIBLE_WALL ||
6439              element == EL_INVISIBLE_SAND)
6440     {
6441       if (game.light_time_left > 0)
6442         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6443
6444       TEST_DrawLevelField(x, y);
6445
6446       /* uncrumble neighbour fields, if needed */
6447       if (element == EL_INVISIBLE_SAND)
6448         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6449     }
6450     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6451              element == EL_INVISIBLE_WALL_ACTIVE ||
6452              element == EL_INVISIBLE_SAND_ACTIVE)
6453     {
6454       if (game.light_time_left == 0)
6455         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6456
6457       TEST_DrawLevelField(x, y);
6458
6459       /* re-crumble neighbour fields, if needed */
6460       if (element == EL_INVISIBLE_SAND)
6461         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6462     }
6463   }
6464 }
6465
6466 static void RedrawAllInvisibleElementsForLenses()
6467 {
6468   int x, y;
6469
6470   SCAN_PLAYFIELD(x, y)
6471   {
6472     int element = Feld[x][y];
6473
6474     if (element == EL_EMC_DRIPPER &&
6475         game.lenses_time_left > 0)
6476     {
6477       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6478       TEST_DrawLevelField(x, y);
6479     }
6480     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6481              game.lenses_time_left == 0)
6482     {
6483       Feld[x][y] = EL_EMC_DRIPPER;
6484       TEST_DrawLevelField(x, y);
6485     }
6486     else if (element == EL_INVISIBLE_STEELWALL ||
6487              element == EL_INVISIBLE_WALL ||
6488              element == EL_INVISIBLE_SAND)
6489     {
6490       if (game.lenses_time_left > 0)
6491         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6492
6493       TEST_DrawLevelField(x, y);
6494
6495       /* uncrumble neighbour fields, if needed */
6496       if (element == EL_INVISIBLE_SAND)
6497         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6498     }
6499     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6500              element == EL_INVISIBLE_WALL_ACTIVE ||
6501              element == EL_INVISIBLE_SAND_ACTIVE)
6502     {
6503       if (game.lenses_time_left == 0)
6504         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6505
6506       TEST_DrawLevelField(x, y);
6507
6508       /* re-crumble neighbour fields, if needed */
6509       if (element == EL_INVISIBLE_SAND)
6510         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6511     }
6512   }
6513 }
6514
6515 static void RedrawAllInvisibleElementsForMagnifier()
6516 {
6517   int x, y;
6518
6519   SCAN_PLAYFIELD(x, y)
6520   {
6521     int element = Feld[x][y];
6522
6523     if (element == EL_EMC_FAKE_GRASS &&
6524         game.magnify_time_left > 0)
6525     {
6526       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6527       TEST_DrawLevelField(x, y);
6528     }
6529     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6530              game.magnify_time_left == 0)
6531     {
6532       Feld[x][y] = EL_EMC_FAKE_GRASS;
6533       TEST_DrawLevelField(x, y);
6534     }
6535     else if (IS_GATE_GRAY(element) &&
6536              game.magnify_time_left > 0)
6537     {
6538       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6539                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6540                     IS_EM_GATE_GRAY(element) ?
6541                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6542                     IS_EMC_GATE_GRAY(element) ?
6543                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6544                     IS_DC_GATE_GRAY(element) ?
6545                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6546                     element);
6547       TEST_DrawLevelField(x, y);
6548     }
6549     else if (IS_GATE_GRAY_ACTIVE(element) &&
6550              game.magnify_time_left == 0)
6551     {
6552       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6553                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6554                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6555                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6556                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6557                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6558                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6559                     EL_DC_GATE_WHITE_GRAY :
6560                     element);
6561       TEST_DrawLevelField(x, y);
6562     }
6563   }
6564 }
6565
6566 static void ToggleLightSwitch(int x, int y)
6567 {
6568   int element = Feld[x][y];
6569
6570   game.light_time_left =
6571     (element == EL_LIGHT_SWITCH ?
6572      level.time_light * FRAMES_PER_SECOND : 0);
6573
6574   RedrawAllLightSwitchesAndInvisibleElements();
6575 }
6576
6577 static void ActivateTimegateSwitch(int x, int y)
6578 {
6579   int xx, yy;
6580
6581   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6582
6583   SCAN_PLAYFIELD(xx, yy)
6584   {
6585     int element = Feld[xx][yy];
6586
6587     if (element == EL_TIMEGATE_CLOSED ||
6588         element == EL_TIMEGATE_CLOSING)
6589     {
6590       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6591       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6592     }
6593
6594     /*
6595     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6596     {
6597       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6598       TEST_DrawLevelField(xx, yy);
6599     }
6600     */
6601
6602   }
6603
6604 #if 1
6605   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6606                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6607 #else
6608   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6609 #endif
6610 }
6611
6612 void Impact(int x, int y)
6613 {
6614   boolean last_line = (y == lev_fieldy - 1);
6615   boolean object_hit = FALSE;
6616   boolean impact = (last_line || object_hit);
6617   int element = Feld[x][y];
6618   int smashed = EL_STEELWALL;
6619
6620   if (!last_line)       /* check if element below was hit */
6621   {
6622     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6623       return;
6624
6625     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6626                                          MovDir[x][y + 1] != MV_DOWN ||
6627                                          MovPos[x][y + 1] <= TILEY / 2));
6628
6629     /* do not smash moving elements that left the smashed field in time */
6630     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6631         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6632       object_hit = FALSE;
6633
6634 #if USE_QUICKSAND_IMPACT_BUGFIX
6635     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6636     {
6637       RemoveMovingField(x, y + 1);
6638       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6639       Feld[x][y + 2] = EL_ROCK;
6640       TEST_DrawLevelField(x, y + 2);
6641
6642       object_hit = TRUE;
6643     }
6644
6645     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6646     {
6647       RemoveMovingField(x, y + 1);
6648       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6649       Feld[x][y + 2] = EL_ROCK;
6650       TEST_DrawLevelField(x, y + 2);
6651
6652       object_hit = TRUE;
6653     }
6654 #endif
6655
6656     if (object_hit)
6657       smashed = MovingOrBlocked2Element(x, y + 1);
6658
6659     impact = (last_line || object_hit);
6660   }
6661
6662   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6663   {
6664     SplashAcid(x, y + 1);
6665     return;
6666   }
6667
6668   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6669   /* only reset graphic animation if graphic really changes after impact */
6670   if (impact &&
6671       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6672   {
6673     ResetGfxAnimation(x, y);
6674     TEST_DrawLevelField(x, y);
6675   }
6676
6677   if (impact && CAN_EXPLODE_IMPACT(element))
6678   {
6679     Bang(x, y);
6680     return;
6681   }
6682   else if (impact && element == EL_PEARL &&
6683            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6684   {
6685     ResetGfxAnimation(x, y);
6686
6687     Feld[x][y] = EL_PEARL_BREAKING;
6688     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6689     return;
6690   }
6691   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6692   {
6693     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6694
6695     return;
6696   }
6697
6698   if (impact && element == EL_AMOEBA_DROP)
6699   {
6700     if (object_hit && IS_PLAYER(x, y + 1))
6701       KillPlayerUnlessEnemyProtected(x, y + 1);
6702     else if (object_hit && smashed == EL_PENGUIN)
6703       Bang(x, y + 1);
6704     else
6705     {
6706       Feld[x][y] = EL_AMOEBA_GROWING;
6707       Store[x][y] = EL_AMOEBA_WET;
6708
6709       ResetRandomAnimationValue(x, y);
6710     }
6711     return;
6712   }
6713
6714   if (object_hit)               /* check which object was hit */
6715   {
6716     if ((CAN_PASS_MAGIC_WALL(element) && 
6717          (smashed == EL_MAGIC_WALL ||
6718           smashed == EL_BD_MAGIC_WALL)) ||
6719         (CAN_PASS_DC_MAGIC_WALL(element) &&
6720          smashed == EL_DC_MAGIC_WALL))
6721     {
6722       int xx, yy;
6723       int activated_magic_wall =
6724         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6725          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6726          EL_DC_MAGIC_WALL_ACTIVE);
6727
6728       /* activate magic wall / mill */
6729       SCAN_PLAYFIELD(xx, yy)
6730       {
6731         if (Feld[xx][yy] == smashed)
6732           Feld[xx][yy] = activated_magic_wall;
6733       }
6734
6735       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6736       game.magic_wall_active = TRUE;
6737
6738       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6739                             SND_MAGIC_WALL_ACTIVATING :
6740                             smashed == EL_BD_MAGIC_WALL ?
6741                             SND_BD_MAGIC_WALL_ACTIVATING :
6742                             SND_DC_MAGIC_WALL_ACTIVATING));
6743     }
6744
6745     if (IS_PLAYER(x, y + 1))
6746     {
6747       if (CAN_SMASH_PLAYER(element))
6748       {
6749         KillPlayerUnlessEnemyProtected(x, y + 1);
6750         return;
6751       }
6752     }
6753     else if (smashed == EL_PENGUIN)
6754     {
6755       if (CAN_SMASH_PLAYER(element))
6756       {
6757         Bang(x, y + 1);
6758         return;
6759       }
6760     }
6761     else if (element == EL_BD_DIAMOND)
6762     {
6763       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6764       {
6765         Bang(x, y + 1);
6766         return;
6767       }
6768     }
6769     else if (((element == EL_SP_INFOTRON ||
6770                element == EL_SP_ZONK) &&
6771               (smashed == EL_SP_SNIKSNAK ||
6772                smashed == EL_SP_ELECTRON ||
6773                smashed == EL_SP_DISK_ORANGE)) ||
6774              (element == EL_SP_INFOTRON &&
6775               smashed == EL_SP_DISK_YELLOW))
6776     {
6777       Bang(x, y + 1);
6778       return;
6779     }
6780     else if (CAN_SMASH_EVERYTHING(element))
6781     {
6782       if (IS_CLASSIC_ENEMY(smashed) ||
6783           CAN_EXPLODE_SMASHED(smashed))
6784       {
6785         Bang(x, y + 1);
6786         return;
6787       }
6788       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6789       {
6790         if (smashed == EL_LAMP ||
6791             smashed == EL_LAMP_ACTIVE)
6792         {
6793           Bang(x, y + 1);
6794           return;
6795         }
6796         else if (smashed == EL_NUT)
6797         {
6798           Feld[x][y + 1] = EL_NUT_BREAKING;
6799           PlayLevelSound(x, y, SND_NUT_BREAKING);
6800           RaiseScoreElement(EL_NUT);
6801           return;
6802         }
6803         else if (smashed == EL_PEARL)
6804         {
6805           ResetGfxAnimation(x, y);
6806
6807           Feld[x][y + 1] = EL_PEARL_BREAKING;
6808           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6809           return;
6810         }
6811         else if (smashed == EL_DIAMOND)
6812         {
6813           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6814           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6815           return;
6816         }
6817         else if (IS_BELT_SWITCH(smashed))
6818         {
6819           ToggleBeltSwitch(x, y + 1);
6820         }
6821         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6822                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6823                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6824                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6825         {
6826           ToggleSwitchgateSwitch(x, y + 1);
6827         }
6828         else if (smashed == EL_LIGHT_SWITCH ||
6829                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6830         {
6831           ToggleLightSwitch(x, y + 1);
6832         }
6833         else
6834         {
6835 #if 0
6836           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6837 #endif
6838
6839           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6840
6841           CheckElementChangeBySide(x, y + 1, smashed, element,
6842                                    CE_SWITCHED, CH_SIDE_TOP);
6843           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6844                                             CH_SIDE_TOP);
6845         }
6846       }
6847       else
6848       {
6849         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6850       }
6851     }
6852   }
6853
6854   /* play sound of magic wall / mill */
6855   if (!last_line &&
6856       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6857        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6858        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6859   {
6860     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6861       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6862     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6863       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6864     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6865       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6866
6867     return;
6868   }
6869
6870   /* play sound of object that hits the ground */
6871   if (last_line || object_hit)
6872     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6873 }
6874
6875 inline static void TurnRoundExt(int x, int y)
6876 {
6877   static struct
6878   {
6879     int dx, dy;
6880   } move_xy[] =
6881   {
6882     {  0,  0 },
6883     { -1,  0 },
6884     { +1,  0 },
6885     {  0,  0 },
6886     {  0, -1 },
6887     {  0,  0 }, { 0, 0 }, { 0, 0 },
6888     {  0, +1 }
6889   };
6890   static struct
6891   {
6892     int left, right, back;
6893   } turn[] =
6894   {
6895     { 0,        0,              0        },
6896     { MV_DOWN,  MV_UP,          MV_RIGHT },
6897     { MV_UP,    MV_DOWN,        MV_LEFT  },
6898     { 0,        0,              0        },
6899     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6900     { 0,        0,              0        },
6901     { 0,        0,              0        },
6902     { 0,        0,              0        },
6903     { MV_RIGHT, MV_LEFT,        MV_UP    }
6904   };
6905
6906   int element = Feld[x][y];
6907   int move_pattern = element_info[element].move_pattern;
6908
6909   int old_move_dir = MovDir[x][y];
6910   int left_dir  = turn[old_move_dir].left;
6911   int right_dir = turn[old_move_dir].right;
6912   int back_dir  = turn[old_move_dir].back;
6913
6914   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6915   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6916   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6917   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6918
6919   int left_x  = x + left_dx,  left_y  = y + left_dy;
6920   int right_x = x + right_dx, right_y = y + right_dy;
6921   int move_x  = x + move_dx,  move_y  = y + move_dy;
6922
6923   int xx, yy;
6924
6925   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6926   {
6927     TestIfBadThingTouchesOtherBadThing(x, y);
6928
6929     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6930       MovDir[x][y] = right_dir;
6931     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6932       MovDir[x][y] = left_dir;
6933
6934     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6935       MovDelay[x][y] = 9;
6936     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6937       MovDelay[x][y] = 1;
6938   }
6939   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6940   {
6941     TestIfBadThingTouchesOtherBadThing(x, y);
6942
6943     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6944       MovDir[x][y] = left_dir;
6945     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6946       MovDir[x][y] = right_dir;
6947
6948     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6949       MovDelay[x][y] = 9;
6950     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6951       MovDelay[x][y] = 1;
6952   }
6953   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6954   {
6955     TestIfBadThingTouchesOtherBadThing(x, y);
6956
6957     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6958       MovDir[x][y] = left_dir;
6959     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6960       MovDir[x][y] = right_dir;
6961
6962     if (MovDir[x][y] != old_move_dir)
6963       MovDelay[x][y] = 9;
6964   }
6965   else if (element == EL_YAMYAM)
6966   {
6967     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6968     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6969
6970     if (can_turn_left && can_turn_right)
6971       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6972     else if (can_turn_left)
6973       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6974     else if (can_turn_right)
6975       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6976     else
6977       MovDir[x][y] = back_dir;
6978
6979     MovDelay[x][y] = 16 + 16 * RND(3);
6980   }
6981   else if (element == EL_DARK_YAMYAM)
6982   {
6983     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6984                                                          left_x, left_y);
6985     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6986                                                          right_x, right_y);
6987
6988     if (can_turn_left && can_turn_right)
6989       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6990     else if (can_turn_left)
6991       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6992     else if (can_turn_right)
6993       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6994     else
6995       MovDir[x][y] = back_dir;
6996
6997     MovDelay[x][y] = 16 + 16 * RND(3);
6998   }
6999   else if (element == EL_PACMAN)
7000   {
7001     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7002     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7003
7004     if (can_turn_left && can_turn_right)
7005       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7006     else if (can_turn_left)
7007       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7008     else if (can_turn_right)
7009       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7010     else
7011       MovDir[x][y] = back_dir;
7012
7013     MovDelay[x][y] = 6 + RND(40);
7014   }
7015   else if (element == EL_PIG)
7016   {
7017     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7018     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7019     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7020     boolean should_turn_left, should_turn_right, should_move_on;
7021     int rnd_value = 24;
7022     int rnd = RND(rnd_value);
7023
7024     should_turn_left = (can_turn_left &&
7025                         (!can_move_on ||
7026                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7027                                                    y + back_dy + left_dy)));
7028     should_turn_right = (can_turn_right &&
7029                          (!can_move_on ||
7030                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7031                                                     y + back_dy + right_dy)));
7032     should_move_on = (can_move_on &&
7033                       (!can_turn_left ||
7034                        !can_turn_right ||
7035                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7036                                                  y + move_dy + left_dy) ||
7037                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7038                                                  y + move_dy + right_dy)));
7039
7040     if (should_turn_left || should_turn_right || should_move_on)
7041     {
7042       if (should_turn_left && should_turn_right && should_move_on)
7043         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7044                         rnd < 2 * rnd_value / 3 ? right_dir :
7045                         old_move_dir);
7046       else if (should_turn_left && should_turn_right)
7047         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7048       else if (should_turn_left && should_move_on)
7049         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7050       else if (should_turn_right && should_move_on)
7051         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7052       else if (should_turn_left)
7053         MovDir[x][y] = left_dir;
7054       else if (should_turn_right)
7055         MovDir[x][y] = right_dir;
7056       else if (should_move_on)
7057         MovDir[x][y] = old_move_dir;
7058     }
7059     else if (can_move_on && rnd > rnd_value / 8)
7060       MovDir[x][y] = old_move_dir;
7061     else if (can_turn_left && can_turn_right)
7062       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7063     else if (can_turn_left && rnd > rnd_value / 8)
7064       MovDir[x][y] = left_dir;
7065     else if (can_turn_right && rnd > rnd_value/8)
7066       MovDir[x][y] = right_dir;
7067     else
7068       MovDir[x][y] = back_dir;
7069
7070     xx = x + move_xy[MovDir[x][y]].dx;
7071     yy = y + move_xy[MovDir[x][y]].dy;
7072
7073     if (!IN_LEV_FIELD(xx, yy) ||
7074         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7075       MovDir[x][y] = old_move_dir;
7076
7077     MovDelay[x][y] = 0;
7078   }
7079   else if (element == EL_DRAGON)
7080   {
7081     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7082     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7083     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7084     int rnd_value = 24;
7085     int rnd = RND(rnd_value);
7086
7087     if (can_move_on && rnd > rnd_value / 8)
7088       MovDir[x][y] = old_move_dir;
7089     else if (can_turn_left && can_turn_right)
7090       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7091     else if (can_turn_left && rnd > rnd_value / 8)
7092       MovDir[x][y] = left_dir;
7093     else if (can_turn_right && rnd > rnd_value / 8)
7094       MovDir[x][y] = right_dir;
7095     else
7096       MovDir[x][y] = back_dir;
7097
7098     xx = x + move_xy[MovDir[x][y]].dx;
7099     yy = y + move_xy[MovDir[x][y]].dy;
7100
7101     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7102       MovDir[x][y] = old_move_dir;
7103
7104     MovDelay[x][y] = 0;
7105   }
7106   else if (element == EL_MOLE)
7107   {
7108     boolean can_move_on =
7109       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7110                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7111                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7112     if (!can_move_on)
7113     {
7114       boolean can_turn_left =
7115         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7116                               IS_AMOEBOID(Feld[left_x][left_y])));
7117
7118       boolean can_turn_right =
7119         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7120                               IS_AMOEBOID(Feld[right_x][right_y])));
7121
7122       if (can_turn_left && can_turn_right)
7123         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7124       else if (can_turn_left)
7125         MovDir[x][y] = left_dir;
7126       else
7127         MovDir[x][y] = right_dir;
7128     }
7129
7130     if (MovDir[x][y] != old_move_dir)
7131       MovDelay[x][y] = 9;
7132   }
7133   else if (element == EL_BALLOON)
7134   {
7135     MovDir[x][y] = game.wind_direction;
7136     MovDelay[x][y] = 0;
7137   }
7138   else if (element == EL_SPRING)
7139   {
7140 #if USE_NEW_SPRING_BUMPER
7141     if (MovDir[x][y] & MV_HORIZONTAL)
7142     {
7143       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7144           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7145       {
7146         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7147         ResetGfxAnimation(move_x, move_y);
7148         TEST_DrawLevelField(move_x, move_y);
7149
7150         MovDir[x][y] = back_dir;
7151       }
7152       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7153                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7154         MovDir[x][y] = MV_NONE;
7155     }
7156 #else
7157     if (MovDir[x][y] & MV_HORIZONTAL &&
7158         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7159          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7160       MovDir[x][y] = MV_NONE;
7161 #endif
7162
7163     MovDelay[x][y] = 0;
7164   }
7165   else if (element == EL_ROBOT ||
7166            element == EL_SATELLITE ||
7167            element == EL_PENGUIN ||
7168            element == EL_EMC_ANDROID)
7169   {
7170     int attr_x = -1, attr_y = -1;
7171
7172     if (AllPlayersGone)
7173     {
7174       attr_x = ExitX;
7175       attr_y = ExitY;
7176     }
7177     else
7178     {
7179       int i;
7180
7181       for (i = 0; i < MAX_PLAYERS; i++)
7182       {
7183         struct PlayerInfo *player = &stored_player[i];
7184         int jx = player->jx, jy = player->jy;
7185
7186         if (!player->active)
7187           continue;
7188
7189         if (attr_x == -1 ||
7190             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7191         {
7192           attr_x = jx;
7193           attr_y = jy;
7194         }
7195       }
7196     }
7197
7198     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7199         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7200          game.engine_version < VERSION_IDENT(3,1,0,0)))
7201     {
7202       attr_x = ZX;
7203       attr_y = ZY;
7204     }
7205
7206     if (element == EL_PENGUIN)
7207     {
7208       int i;
7209       static int xy[4][2] =
7210       {
7211         { 0, -1 },
7212         { -1, 0 },
7213         { +1, 0 },
7214         { 0, +1 }
7215       };
7216
7217       for (i = 0; i < NUM_DIRECTIONS; i++)
7218       {
7219         int ex = x + xy[i][0];
7220         int ey = y + xy[i][1];
7221
7222         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7223                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7224                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7225                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7226         {
7227           attr_x = ex;
7228           attr_y = ey;
7229           break;
7230         }
7231       }
7232     }
7233
7234     MovDir[x][y] = MV_NONE;
7235     if (attr_x < x)
7236       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7237     else if (attr_x > x)
7238       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7239     if (attr_y < y)
7240       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7241     else if (attr_y > y)
7242       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7243
7244     if (element == EL_ROBOT)
7245     {
7246       int newx, newy;
7247
7248       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7249         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7250       Moving2Blocked(x, y, &newx, &newy);
7251
7252       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7253         MovDelay[x][y] = 8 + 8 * !RND(3);
7254       else
7255         MovDelay[x][y] = 16;
7256     }
7257     else if (element == EL_PENGUIN)
7258     {
7259       int newx, newy;
7260
7261       MovDelay[x][y] = 1;
7262
7263       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7264       {
7265         boolean first_horiz = RND(2);
7266         int new_move_dir = MovDir[x][y];
7267
7268         MovDir[x][y] =
7269           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7270         Moving2Blocked(x, y, &newx, &newy);
7271
7272         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7273           return;
7274
7275         MovDir[x][y] =
7276           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7277         Moving2Blocked(x, y, &newx, &newy);
7278
7279         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7280           return;
7281
7282         MovDir[x][y] = old_move_dir;
7283         return;
7284       }
7285     }
7286     else if (element == EL_SATELLITE)
7287     {
7288       int newx, newy;
7289
7290       MovDelay[x][y] = 1;
7291
7292       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7293       {
7294         boolean first_horiz = RND(2);
7295         int new_move_dir = MovDir[x][y];
7296
7297         MovDir[x][y] =
7298           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7299         Moving2Blocked(x, y, &newx, &newy);
7300
7301         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7302           return;
7303
7304         MovDir[x][y] =
7305           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7306         Moving2Blocked(x, y, &newx, &newy);
7307
7308         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7309           return;
7310
7311         MovDir[x][y] = old_move_dir;
7312         return;
7313       }
7314     }
7315     else if (element == EL_EMC_ANDROID)
7316     {
7317       static int check_pos[16] =
7318       {
7319         -1,             /*  0 => (invalid)          */
7320         7,              /*  1 => MV_LEFT            */
7321         3,              /*  2 => MV_RIGHT           */
7322         -1,             /*  3 => (invalid)          */
7323         1,              /*  4 =>            MV_UP   */
7324         0,              /*  5 => MV_LEFT  | MV_UP   */
7325         2,              /*  6 => MV_RIGHT | MV_UP   */
7326         -1,             /*  7 => (invalid)          */
7327         5,              /*  8 =>            MV_DOWN */
7328         6,              /*  9 => MV_LEFT  | MV_DOWN */
7329         4,              /* 10 => MV_RIGHT | MV_DOWN */
7330         -1,             /* 11 => (invalid)          */
7331         -1,             /* 12 => (invalid)          */
7332         -1,             /* 13 => (invalid)          */
7333         -1,             /* 14 => (invalid)          */
7334         -1,             /* 15 => (invalid)          */
7335       };
7336       static struct
7337       {
7338         int dx, dy;
7339         int dir;
7340       } check_xy[8] =
7341       {
7342         { -1, -1,       MV_LEFT  | MV_UP   },
7343         {  0, -1,                  MV_UP   },
7344         { +1, -1,       MV_RIGHT | MV_UP   },
7345         { +1,  0,       MV_RIGHT           },
7346         { +1, +1,       MV_RIGHT | MV_DOWN },
7347         {  0, +1,                  MV_DOWN },
7348         { -1, +1,       MV_LEFT  | MV_DOWN },
7349         { -1,  0,       MV_LEFT            },
7350       };
7351       int start_pos, check_order;
7352       boolean can_clone = FALSE;
7353       int i;
7354
7355       /* check if there is any free field around current position */
7356       for (i = 0; i < 8; i++)
7357       {
7358         int newx = x + check_xy[i].dx;
7359         int newy = y + check_xy[i].dy;
7360
7361         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7362         {
7363           can_clone = TRUE;
7364
7365           break;
7366         }
7367       }
7368
7369       if (can_clone)            /* randomly find an element to clone */
7370       {
7371         can_clone = FALSE;
7372
7373         start_pos = check_pos[RND(8)];
7374         check_order = (RND(2) ? -1 : +1);
7375
7376         for (i = 0; i < 8; i++)
7377         {
7378           int pos_raw = start_pos + i * check_order;
7379           int pos = (pos_raw + 8) % 8;
7380           int newx = x + check_xy[pos].dx;
7381           int newy = y + check_xy[pos].dy;
7382
7383           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7384           {
7385             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7386             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7387
7388             Store[x][y] = Feld[newx][newy];
7389
7390             can_clone = TRUE;
7391
7392             break;
7393           }
7394         }
7395       }
7396
7397       if (can_clone)            /* randomly find a direction to move */
7398       {
7399         can_clone = FALSE;
7400
7401         start_pos = check_pos[RND(8)];
7402         check_order = (RND(2) ? -1 : +1);
7403
7404         for (i = 0; i < 8; i++)
7405         {
7406           int pos_raw = start_pos + i * check_order;
7407           int pos = (pos_raw + 8) % 8;
7408           int newx = x + check_xy[pos].dx;
7409           int newy = y + check_xy[pos].dy;
7410           int new_move_dir = check_xy[pos].dir;
7411
7412           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7413           {
7414             MovDir[x][y] = new_move_dir;
7415             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7416
7417             can_clone = TRUE;
7418
7419             break;
7420           }
7421         }
7422       }
7423
7424       if (can_clone)            /* cloning and moving successful */
7425         return;
7426
7427       /* cannot clone -- try to move towards player */
7428
7429       start_pos = check_pos[MovDir[x][y] & 0x0f];
7430       check_order = (RND(2) ? -1 : +1);
7431
7432       for (i = 0; i < 3; i++)
7433       {
7434         /* first check start_pos, then previous/next or (next/previous) pos */
7435         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7436         int pos = (pos_raw + 8) % 8;
7437         int newx = x + check_xy[pos].dx;
7438         int newy = y + check_xy[pos].dy;
7439         int new_move_dir = check_xy[pos].dir;
7440
7441         if (IS_PLAYER(newx, newy))
7442           break;
7443
7444         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7445         {
7446           MovDir[x][y] = new_move_dir;
7447           MovDelay[x][y] = level.android_move_time * 8 + 1;
7448
7449           break;
7450         }
7451       }
7452     }
7453   }
7454   else if (move_pattern == MV_TURNING_LEFT ||
7455            move_pattern == MV_TURNING_RIGHT ||
7456            move_pattern == MV_TURNING_LEFT_RIGHT ||
7457            move_pattern == MV_TURNING_RIGHT_LEFT ||
7458            move_pattern == MV_TURNING_RANDOM ||
7459            move_pattern == MV_ALL_DIRECTIONS)
7460   {
7461     boolean can_turn_left =
7462       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7463     boolean can_turn_right =
7464       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7465
7466     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7467       return;
7468
7469     if (move_pattern == MV_TURNING_LEFT)
7470       MovDir[x][y] = left_dir;
7471     else if (move_pattern == MV_TURNING_RIGHT)
7472       MovDir[x][y] = right_dir;
7473     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7474       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7475     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7476       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7477     else if (move_pattern == MV_TURNING_RANDOM)
7478       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7479                       can_turn_right && !can_turn_left ? right_dir :
7480                       RND(2) ? left_dir : right_dir);
7481     else if (can_turn_left && can_turn_right)
7482       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7483     else if (can_turn_left)
7484       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7485     else if (can_turn_right)
7486       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7487     else
7488       MovDir[x][y] = back_dir;
7489
7490     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7491   }
7492   else if (move_pattern == MV_HORIZONTAL ||
7493            move_pattern == MV_VERTICAL)
7494   {
7495     if (move_pattern & old_move_dir)
7496       MovDir[x][y] = back_dir;
7497     else if (move_pattern == MV_HORIZONTAL)
7498       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7499     else if (move_pattern == MV_VERTICAL)
7500       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7501
7502     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7503   }
7504   else if (move_pattern & MV_ANY_DIRECTION)
7505   {
7506     MovDir[x][y] = move_pattern;
7507     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7508   }
7509   else if (move_pattern & MV_WIND_DIRECTION)
7510   {
7511     MovDir[x][y] = game.wind_direction;
7512     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7513   }
7514   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7515   {
7516     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7517       MovDir[x][y] = left_dir;
7518     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7519       MovDir[x][y] = right_dir;
7520
7521     if (MovDir[x][y] != old_move_dir)
7522       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7523   }
7524   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7525   {
7526     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7527       MovDir[x][y] = right_dir;
7528     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7529       MovDir[x][y] = left_dir;
7530
7531     if (MovDir[x][y] != old_move_dir)
7532       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7533   }
7534   else if (move_pattern == MV_TOWARDS_PLAYER ||
7535            move_pattern == MV_AWAY_FROM_PLAYER)
7536   {
7537     int attr_x = -1, attr_y = -1;
7538     int newx, newy;
7539     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7540
7541     if (AllPlayersGone)
7542     {
7543       attr_x = ExitX;
7544       attr_y = ExitY;
7545     }
7546     else
7547     {
7548       int i;
7549
7550       for (i = 0; i < MAX_PLAYERS; i++)
7551       {
7552         struct PlayerInfo *player = &stored_player[i];
7553         int jx = player->jx, jy = player->jy;
7554
7555         if (!player->active)
7556           continue;
7557
7558         if (attr_x == -1 ||
7559             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7560         {
7561           attr_x = jx;
7562           attr_y = jy;
7563         }
7564       }
7565     }
7566
7567     MovDir[x][y] = MV_NONE;
7568     if (attr_x < x)
7569       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7570     else if (attr_x > x)
7571       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7572     if (attr_y < y)
7573       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7574     else if (attr_y > y)
7575       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7576
7577     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7578
7579     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7580     {
7581       boolean first_horiz = RND(2);
7582       int new_move_dir = MovDir[x][y];
7583
7584       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7585       {
7586         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7587         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7588
7589         return;
7590       }
7591
7592       MovDir[x][y] =
7593         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7594       Moving2Blocked(x, y, &newx, &newy);
7595
7596       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7597         return;
7598
7599       MovDir[x][y] =
7600         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7601       Moving2Blocked(x, y, &newx, &newy);
7602
7603       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7604         return;
7605
7606       MovDir[x][y] = old_move_dir;
7607     }
7608   }
7609   else if (move_pattern == MV_WHEN_PUSHED ||
7610            move_pattern == MV_WHEN_DROPPED)
7611   {
7612     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7613       MovDir[x][y] = MV_NONE;
7614
7615     MovDelay[x][y] = 0;
7616   }
7617   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7618   {
7619     static int test_xy[7][2] =
7620     {
7621       { 0, -1 },
7622       { -1, 0 },
7623       { +1, 0 },
7624       { 0, +1 },
7625       { 0, -1 },
7626       { -1, 0 },
7627       { +1, 0 },
7628     };
7629     static int test_dir[7] =
7630     {
7631       MV_UP,
7632       MV_LEFT,
7633       MV_RIGHT,
7634       MV_DOWN,
7635       MV_UP,
7636       MV_LEFT,
7637       MV_RIGHT,
7638     };
7639     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7640     int move_preference = -1000000;     /* start with very low preference */
7641     int new_move_dir = MV_NONE;
7642     int start_test = RND(4);
7643     int i;
7644
7645     for (i = 0; i < NUM_DIRECTIONS; i++)
7646     {
7647       int move_dir = test_dir[start_test + i];
7648       int move_dir_preference;
7649
7650       xx = x + test_xy[start_test + i][0];
7651       yy = y + test_xy[start_test + i][1];
7652
7653       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7654           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7655       {
7656         new_move_dir = move_dir;
7657
7658         break;
7659       }
7660
7661       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7662         continue;
7663
7664       move_dir_preference = -1 * RunnerVisit[xx][yy];
7665       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7666         move_dir_preference = PlayerVisit[xx][yy];
7667
7668       if (move_dir_preference > move_preference)
7669       {
7670         /* prefer field that has not been visited for the longest time */
7671         move_preference = move_dir_preference;
7672         new_move_dir = move_dir;
7673       }
7674       else if (move_dir_preference == move_preference &&
7675                move_dir == old_move_dir)
7676       {
7677         /* prefer last direction when all directions are preferred equally */
7678         move_preference = move_dir_preference;
7679         new_move_dir = move_dir;
7680       }
7681     }
7682
7683     MovDir[x][y] = new_move_dir;
7684     if (old_move_dir != new_move_dir)
7685       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7686   }
7687 }
7688
7689 static void TurnRound(int x, int y)
7690 {
7691   int direction = MovDir[x][y];
7692
7693   TurnRoundExt(x, y);
7694
7695   GfxDir[x][y] = MovDir[x][y];
7696
7697   if (direction != MovDir[x][y])
7698     GfxFrame[x][y] = 0;
7699
7700   if (MovDelay[x][y])
7701     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7702
7703   ResetGfxFrame(x, y, FALSE);
7704 }
7705
7706 static boolean JustBeingPushed(int x, int y)
7707 {
7708   int i;
7709
7710   for (i = 0; i < MAX_PLAYERS; i++)
7711   {
7712     struct PlayerInfo *player = &stored_player[i];
7713
7714     if (player->active && player->is_pushing && player->MovPos)
7715     {
7716       int next_jx = player->jx + (player->jx - player->last_jx);
7717       int next_jy = player->jy + (player->jy - player->last_jy);
7718
7719       if (x == next_jx && y == next_jy)
7720         return TRUE;
7721     }
7722   }
7723
7724   return FALSE;
7725 }
7726
7727 void StartMoving(int x, int y)
7728 {
7729   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7730   int element = Feld[x][y];
7731
7732   if (Stop[x][y])
7733     return;
7734
7735   if (MovDelay[x][y] == 0)
7736     GfxAction[x][y] = ACTION_DEFAULT;
7737
7738   if (CAN_FALL(element) && y < lev_fieldy - 1)
7739   {
7740     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7741         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7742       if (JustBeingPushed(x, y))
7743         return;
7744
7745     if (element == EL_QUICKSAND_FULL)
7746     {
7747       if (IS_FREE(x, y + 1))
7748       {
7749         InitMovingField(x, y, MV_DOWN);
7750         started_moving = TRUE;
7751
7752         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7753 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7754         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7755           Store[x][y] = EL_ROCK;
7756 #else
7757         Store[x][y] = EL_ROCK;
7758 #endif
7759
7760         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7761       }
7762       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7763       {
7764         if (!MovDelay[x][y])
7765         {
7766           MovDelay[x][y] = TILEY + 1;
7767
7768           ResetGfxAnimation(x, y);
7769           ResetGfxAnimation(x, y + 1);
7770         }
7771
7772         if (MovDelay[x][y])
7773         {
7774           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7775           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7776
7777           MovDelay[x][y]--;
7778           if (MovDelay[x][y])
7779             return;
7780         }
7781
7782         Feld[x][y] = EL_QUICKSAND_EMPTY;
7783         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7784         Store[x][y + 1] = Store[x][y];
7785         Store[x][y] = 0;
7786
7787         PlayLevelSoundAction(x, y, ACTION_FILLING);
7788       }
7789       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7790       {
7791         if (!MovDelay[x][y])
7792         {
7793           MovDelay[x][y] = TILEY + 1;
7794
7795           ResetGfxAnimation(x, y);
7796           ResetGfxAnimation(x, y + 1);
7797         }
7798
7799         if (MovDelay[x][y])
7800         {
7801           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7802           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7803
7804           MovDelay[x][y]--;
7805           if (MovDelay[x][y])
7806             return;
7807         }
7808
7809         Feld[x][y] = EL_QUICKSAND_EMPTY;
7810         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7811         Store[x][y + 1] = Store[x][y];
7812         Store[x][y] = 0;
7813
7814         PlayLevelSoundAction(x, y, ACTION_FILLING);
7815       }
7816     }
7817     else if (element == EL_QUICKSAND_FAST_FULL)
7818     {
7819       if (IS_FREE(x, y + 1))
7820       {
7821         InitMovingField(x, y, MV_DOWN);
7822         started_moving = TRUE;
7823
7824         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7825 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7826         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7827           Store[x][y] = EL_ROCK;
7828 #else
7829         Store[x][y] = EL_ROCK;
7830 #endif
7831
7832         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7833       }
7834       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7835       {
7836         if (!MovDelay[x][y])
7837         {
7838           MovDelay[x][y] = TILEY + 1;
7839
7840           ResetGfxAnimation(x, y);
7841           ResetGfxAnimation(x, y + 1);
7842         }
7843
7844         if (MovDelay[x][y])
7845         {
7846           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7847           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7848
7849           MovDelay[x][y]--;
7850           if (MovDelay[x][y])
7851             return;
7852         }
7853
7854         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7855         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7856         Store[x][y + 1] = Store[x][y];
7857         Store[x][y] = 0;
7858
7859         PlayLevelSoundAction(x, y, ACTION_FILLING);
7860       }
7861       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7862       {
7863         if (!MovDelay[x][y])
7864         {
7865           MovDelay[x][y] = TILEY + 1;
7866
7867           ResetGfxAnimation(x, y);
7868           ResetGfxAnimation(x, y + 1);
7869         }
7870
7871         if (MovDelay[x][y])
7872         {
7873           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7874           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7875
7876           MovDelay[x][y]--;
7877           if (MovDelay[x][y])
7878             return;
7879         }
7880
7881         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7882         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7883         Store[x][y + 1] = Store[x][y];
7884         Store[x][y] = 0;
7885
7886         PlayLevelSoundAction(x, y, ACTION_FILLING);
7887       }
7888     }
7889     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7890              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7891     {
7892       InitMovingField(x, y, MV_DOWN);
7893       started_moving = TRUE;
7894
7895       Feld[x][y] = EL_QUICKSAND_FILLING;
7896       Store[x][y] = element;
7897
7898       PlayLevelSoundAction(x, y, ACTION_FILLING);
7899     }
7900     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7901              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7902     {
7903       InitMovingField(x, y, MV_DOWN);
7904       started_moving = TRUE;
7905
7906       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7907       Store[x][y] = element;
7908
7909       PlayLevelSoundAction(x, y, ACTION_FILLING);
7910     }
7911     else if (element == EL_MAGIC_WALL_FULL)
7912     {
7913       if (IS_FREE(x, y + 1))
7914       {
7915         InitMovingField(x, y, MV_DOWN);
7916         started_moving = TRUE;
7917
7918         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7919         Store[x][y] = EL_CHANGED(Store[x][y]);
7920       }
7921       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7922       {
7923         if (!MovDelay[x][y])
7924           MovDelay[x][y] = TILEY/4 + 1;
7925
7926         if (MovDelay[x][y])
7927         {
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7934         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7935         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7936         Store[x][y] = 0;
7937       }
7938     }
7939     else if (element == EL_BD_MAGIC_WALL_FULL)
7940     {
7941       if (IS_FREE(x, y + 1))
7942       {
7943         InitMovingField(x, y, MV_DOWN);
7944         started_moving = TRUE;
7945
7946         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7947         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7948       }
7949       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7950       {
7951         if (!MovDelay[x][y])
7952           MovDelay[x][y] = TILEY/4 + 1;
7953
7954         if (MovDelay[x][y])
7955         {
7956           MovDelay[x][y]--;
7957           if (MovDelay[x][y])
7958             return;
7959         }
7960
7961         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7962         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7963         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7964         Store[x][y] = 0;
7965       }
7966     }
7967     else if (element == EL_DC_MAGIC_WALL_FULL)
7968     {
7969       if (IS_FREE(x, y + 1))
7970       {
7971         InitMovingField(x, y, MV_DOWN);
7972         started_moving = TRUE;
7973
7974         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7975         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7976       }
7977       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7978       {
7979         if (!MovDelay[x][y])
7980           MovDelay[x][y] = TILEY/4 + 1;
7981
7982         if (MovDelay[x][y])
7983         {
7984           MovDelay[x][y]--;
7985           if (MovDelay[x][y])
7986             return;
7987         }
7988
7989         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7990         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7991         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7992         Store[x][y] = 0;
7993       }
7994     }
7995     else if ((CAN_PASS_MAGIC_WALL(element) &&
7996               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7997                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7998              (CAN_PASS_DC_MAGIC_WALL(element) &&
7999               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8000
8001     {
8002       InitMovingField(x, y, MV_DOWN);
8003       started_moving = TRUE;
8004
8005       Feld[x][y] =
8006         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8007          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8008          EL_DC_MAGIC_WALL_FILLING);
8009       Store[x][y] = element;
8010     }
8011     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8012     {
8013       SplashAcid(x, y + 1);
8014
8015       InitMovingField(x, y, MV_DOWN);
8016       started_moving = TRUE;
8017
8018       Store[x][y] = EL_ACID;
8019     }
8020     else if (
8021 #if USE_FIX_IMPACT_COLLISION
8022              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8023               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8024 #else
8025              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8026               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8027 #endif
8028              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8029               CAN_FALL(element) && WasJustFalling[x][y] &&
8030               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8031
8032              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8033               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8034               (Feld[x][y + 1] == EL_BLOCKED)))
8035     {
8036       /* this is needed for a special case not covered by calling "Impact()"
8037          from "ContinueMoving()": if an element moves to a tile directly below
8038          another element which was just falling on that tile (which was empty
8039          in the previous frame), the falling element above would just stop
8040          instead of smashing the element below (in previous version, the above
8041          element was just checked for "moving" instead of "falling", resulting
8042          in incorrect smashes caused by horizontal movement of the above
8043          element; also, the case of the player being the element to smash was
8044          simply not covered here... :-/ ) */
8045
8046       CheckCollision[x][y] = 0;
8047       CheckImpact[x][y] = 0;
8048
8049       Impact(x, y);
8050     }
8051     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8052     {
8053       if (MovDir[x][y] == MV_NONE)
8054       {
8055         InitMovingField(x, y, MV_DOWN);
8056         started_moving = TRUE;
8057       }
8058     }
8059     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8060     {
8061       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8062         MovDir[x][y] = MV_DOWN;
8063
8064       InitMovingField(x, y, MV_DOWN);
8065       started_moving = TRUE;
8066     }
8067     else if (element == EL_AMOEBA_DROP)
8068     {
8069       Feld[x][y] = EL_AMOEBA_GROWING;
8070       Store[x][y] = EL_AMOEBA_WET;
8071     }
8072     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8073               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8074              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8075              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8076     {
8077       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8078                                 (IS_FREE(x - 1, y + 1) ||
8079                                  Feld[x - 1][y + 1] == EL_ACID));
8080       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8081                                 (IS_FREE(x + 1, y + 1) ||
8082                                  Feld[x + 1][y + 1] == EL_ACID));
8083       boolean can_fall_any  = (can_fall_left || can_fall_right);
8084       boolean can_fall_both = (can_fall_left && can_fall_right);
8085       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8086
8087 #if USE_NEW_ALL_SLIPPERY
8088       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8089       {
8090         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8091           can_fall_right = FALSE;
8092         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8093           can_fall_left = FALSE;
8094         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8095           can_fall_right = FALSE;
8096         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8097           can_fall_left = FALSE;
8098
8099         can_fall_any  = (can_fall_left || can_fall_right);
8100         can_fall_both = FALSE;
8101       }
8102 #else
8103       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8104       {
8105         if (slippery_type == SLIPPERY_ONLY_LEFT)
8106           can_fall_right = FALSE;
8107         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8108           can_fall_left = FALSE;
8109         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8110           can_fall_right = FALSE;
8111         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8112           can_fall_left = FALSE;
8113
8114         can_fall_any  = (can_fall_left || can_fall_right);
8115         can_fall_both = (can_fall_left && can_fall_right);
8116       }
8117 #endif
8118
8119 #if USE_NEW_ALL_SLIPPERY
8120 #else
8121 #if USE_NEW_SP_SLIPPERY
8122       /* !!! better use the same properties as for custom elements here !!! */
8123       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8124                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8125       {
8126         can_fall_right = FALSE;         /* slip down on left side */
8127         can_fall_both = FALSE;
8128       }
8129 #endif
8130 #endif
8131
8132 #if USE_NEW_ALL_SLIPPERY
8133       if (can_fall_both)
8134       {
8135         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8136           can_fall_right = FALSE;       /* slip down on left side */
8137         else
8138           can_fall_left = !(can_fall_right = RND(2));
8139
8140         can_fall_both = FALSE;
8141       }
8142 #else
8143       if (can_fall_both)
8144       {
8145         if (game.emulation == EMU_BOULDERDASH ||
8146             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8147           can_fall_right = FALSE;       /* slip down on left side */
8148         else
8149           can_fall_left = !(can_fall_right = RND(2));
8150
8151         can_fall_both = FALSE;
8152       }
8153 #endif
8154
8155       if (can_fall_any)
8156       {
8157         /* if not determined otherwise, prefer left side for slipping down */
8158         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8159         started_moving = TRUE;
8160       }
8161     }
8162 #if 0
8163     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8164 #else
8165     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8166 #endif
8167     {
8168       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8169       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8170       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8171       int belt_dir = game.belt_dir[belt_nr];
8172
8173       if ((belt_dir == MV_LEFT  && left_is_free) ||
8174           (belt_dir == MV_RIGHT && right_is_free))
8175       {
8176         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8177
8178         InitMovingField(x, y, belt_dir);
8179         started_moving = TRUE;
8180
8181         Pushed[x][y] = TRUE;
8182         Pushed[nextx][y] = TRUE;
8183
8184         GfxAction[x][y] = ACTION_DEFAULT;
8185       }
8186       else
8187       {
8188         MovDir[x][y] = 0;       /* if element was moving, stop it */
8189       }
8190     }
8191   }
8192
8193   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8194 #if 0
8195   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8196 #else
8197   if (CAN_MOVE(element) && !started_moving)
8198 #endif
8199   {
8200     int move_pattern = element_info[element].move_pattern;
8201     int newx, newy;
8202
8203 #if 0
8204 #if DEBUG
8205     if (MovDir[x][y] == MV_NONE)
8206     {
8207       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8208              x, y, element, element_info[element].token_name);
8209       printf("StartMoving(): This should never happen!\n");
8210     }
8211 #endif
8212 #endif
8213
8214     Moving2Blocked(x, y, &newx, &newy);
8215
8216     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8217       return;
8218
8219     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8220         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8221     {
8222       WasJustMoving[x][y] = 0;
8223       CheckCollision[x][y] = 0;
8224
8225       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8226
8227       if (Feld[x][y] != element)        /* element has changed */
8228         return;
8229     }
8230
8231     if (!MovDelay[x][y])        /* start new movement phase */
8232     {
8233       /* all objects that can change their move direction after each step
8234          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8235
8236       if (element != EL_YAMYAM &&
8237           element != EL_DARK_YAMYAM &&
8238           element != EL_PACMAN &&
8239           !(move_pattern & MV_ANY_DIRECTION) &&
8240           move_pattern != MV_TURNING_LEFT &&
8241           move_pattern != MV_TURNING_RIGHT &&
8242           move_pattern != MV_TURNING_LEFT_RIGHT &&
8243           move_pattern != MV_TURNING_RIGHT_LEFT &&
8244           move_pattern != MV_TURNING_RANDOM)
8245       {
8246         TurnRound(x, y);
8247
8248         if (MovDelay[x][y] && (element == EL_BUG ||
8249                                element == EL_SPACESHIP ||
8250                                element == EL_SP_SNIKSNAK ||
8251                                element == EL_SP_ELECTRON ||
8252                                element == EL_MOLE))
8253           TEST_DrawLevelField(x, y);
8254       }
8255     }
8256
8257     if (MovDelay[x][y])         /* wait some time before next movement */
8258     {
8259       MovDelay[x][y]--;
8260
8261       if (element == EL_ROBOT ||
8262           element == EL_YAMYAM ||
8263           element == EL_DARK_YAMYAM)
8264       {
8265         DrawLevelElementAnimationIfNeeded(x, y, element);
8266         PlayLevelSoundAction(x, y, ACTION_WAITING);
8267       }
8268       else if (element == EL_SP_ELECTRON)
8269         DrawLevelElementAnimationIfNeeded(x, y, element);
8270       else if (element == EL_DRAGON)
8271       {
8272         int i;
8273         int dir = MovDir[x][y];
8274         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8275         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8276         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8277                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8278                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8279                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8280         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8281
8282         GfxAction[x][y] = ACTION_ATTACKING;
8283
8284         if (IS_PLAYER(x, y))
8285           DrawPlayerField(x, y);
8286         else
8287           TEST_DrawLevelField(x, y);
8288
8289         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8290
8291         for (i = 1; i <= 3; i++)
8292         {
8293           int xx = x + i * dx;
8294           int yy = y + i * dy;
8295           int sx = SCREENX(xx);
8296           int sy = SCREENY(yy);
8297           int flame_graphic = graphic + (i - 1);
8298
8299           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8300             break;
8301
8302           if (MovDelay[x][y])
8303           {
8304             int flamed = MovingOrBlocked2Element(xx, yy);
8305
8306             /* !!! */
8307 #if 0
8308             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8309               Bang(xx, yy);
8310             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8311               RemoveMovingField(xx, yy);
8312             else
8313               RemoveField(xx, yy);
8314 #else
8315             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8316               Bang(xx, yy);
8317             else
8318               RemoveMovingField(xx, yy);
8319 #endif
8320
8321             ChangeDelay[xx][yy] = 0;
8322
8323             Feld[xx][yy] = EL_FLAMES;
8324
8325             if (IN_SCR_FIELD(sx, sy))
8326             {
8327               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8328               DrawGraphic(sx, sy, flame_graphic, frame);
8329             }
8330           }
8331           else
8332           {
8333             if (Feld[xx][yy] == EL_FLAMES)
8334               Feld[xx][yy] = EL_EMPTY;
8335             TEST_DrawLevelField(xx, yy);
8336           }
8337         }
8338       }
8339
8340       if (MovDelay[x][y])       /* element still has to wait some time */
8341       {
8342         PlayLevelSoundAction(x, y, ACTION_WAITING);
8343
8344         return;
8345       }
8346     }
8347
8348     /* now make next step */
8349
8350     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8351
8352     if (DONT_COLLIDE_WITH(element) &&
8353         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8354         !PLAYER_ENEMY_PROTECTED(newx, newy))
8355     {
8356       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8357
8358       return;
8359     }
8360
8361     else if (CAN_MOVE_INTO_ACID(element) &&
8362              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8363              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8364              (MovDir[x][y] == MV_DOWN ||
8365               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8366     {
8367       SplashAcid(newx, newy);
8368       Store[x][y] = EL_ACID;
8369     }
8370     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8371     {
8372       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8373           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8374           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8375           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8376       {
8377         RemoveField(x, y);
8378         TEST_DrawLevelField(x, y);
8379
8380         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8381         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8382           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8383
8384         local_player->friends_still_needed--;
8385         if (!local_player->friends_still_needed &&
8386             !local_player->GameOver && AllPlayersGone)
8387           PlayerWins(local_player);
8388
8389         return;
8390       }
8391       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8392       {
8393         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8394           TEST_DrawLevelField(newx, newy);
8395         else
8396           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8397       }
8398       else if (!IS_FREE(newx, newy))
8399       {
8400         GfxAction[x][y] = ACTION_WAITING;
8401
8402         if (IS_PLAYER(x, y))
8403           DrawPlayerField(x, y);
8404         else
8405           TEST_DrawLevelField(x, y);
8406
8407         return;
8408       }
8409     }
8410     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8411     {
8412       if (IS_FOOD_PIG(Feld[newx][newy]))
8413       {
8414         if (IS_MOVING(newx, newy))
8415           RemoveMovingField(newx, newy);
8416         else
8417         {
8418           Feld[newx][newy] = EL_EMPTY;
8419           TEST_DrawLevelField(newx, newy);
8420         }
8421
8422         PlayLevelSound(x, y, SND_PIG_DIGGING);
8423       }
8424       else if (!IS_FREE(newx, newy))
8425       {
8426         if (IS_PLAYER(x, y))
8427           DrawPlayerField(x, y);
8428         else
8429           TEST_DrawLevelField(x, y);
8430
8431         return;
8432       }
8433     }
8434     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8435     {
8436       if (Store[x][y] != EL_EMPTY)
8437       {
8438         boolean can_clone = FALSE;
8439         int xx, yy;
8440
8441         /* check if element to clone is still there */
8442         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8443         {
8444           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8445           {
8446             can_clone = TRUE;
8447
8448             break;
8449           }
8450         }
8451
8452         /* cannot clone or target field not free anymore -- do not clone */
8453         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8454           Store[x][y] = EL_EMPTY;
8455       }
8456
8457       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8458       {
8459         if (IS_MV_DIAGONAL(MovDir[x][y]))
8460         {
8461           int diagonal_move_dir = MovDir[x][y];
8462           int stored = Store[x][y];
8463           int change_delay = 8;
8464           int graphic;
8465
8466           /* android is moving diagonally */
8467
8468           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8469
8470           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8471           GfxElement[x][y] = EL_EMC_ANDROID;
8472           GfxAction[x][y] = ACTION_SHRINKING;
8473           GfxDir[x][y] = diagonal_move_dir;
8474           ChangeDelay[x][y] = change_delay;
8475
8476           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8477                                    GfxDir[x][y]);
8478
8479           DrawLevelGraphicAnimation(x, y, graphic);
8480           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8481
8482           if (Feld[newx][newy] == EL_ACID)
8483           {
8484             SplashAcid(newx, newy);
8485
8486             return;
8487           }
8488
8489           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8490
8491           Store[newx][newy] = EL_EMC_ANDROID;
8492           GfxElement[newx][newy] = EL_EMC_ANDROID;
8493           GfxAction[newx][newy] = ACTION_GROWING;
8494           GfxDir[newx][newy] = diagonal_move_dir;
8495           ChangeDelay[newx][newy] = change_delay;
8496
8497           graphic = el_act_dir2img(GfxElement[newx][newy],
8498                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8499
8500           DrawLevelGraphicAnimation(newx, newy, graphic);
8501           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8502
8503           return;
8504         }
8505         else
8506         {
8507           Feld[newx][newy] = EL_EMPTY;
8508           TEST_DrawLevelField(newx, newy);
8509
8510           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8511         }
8512       }
8513       else if (!IS_FREE(newx, newy))
8514       {
8515 #if 0
8516         if (IS_PLAYER(x, y))
8517           DrawPlayerField(x, y);
8518         else
8519           TEST_DrawLevelField(x, y);
8520 #endif
8521
8522         return;
8523       }
8524     }
8525     else if (IS_CUSTOM_ELEMENT(element) &&
8526              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8527     {
8528 #if 1
8529       if (!DigFieldByCE(newx, newy, element))
8530         return;
8531 #else
8532       int new_element = Feld[newx][newy];
8533
8534       if (!IS_FREE(newx, newy))
8535       {
8536         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8537                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8538                       ACTION_BREAKING);
8539
8540         /* no element can dig solid indestructible elements */
8541         if (IS_INDESTRUCTIBLE(new_element) &&
8542             !IS_DIGGABLE(new_element) &&
8543             !IS_COLLECTIBLE(new_element))
8544           return;
8545
8546         if (AmoebaNr[newx][newy] &&
8547             (new_element == EL_AMOEBA_FULL ||
8548              new_element == EL_BD_AMOEBA ||
8549              new_element == EL_AMOEBA_GROWING))
8550         {
8551           AmoebaCnt[AmoebaNr[newx][newy]]--;
8552           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8553         }
8554
8555         if (IS_MOVING(newx, newy))
8556           RemoveMovingField(newx, newy);
8557         else
8558         {
8559           RemoveField(newx, newy);
8560           TEST_DrawLevelField(newx, newy);
8561         }
8562
8563         /* if digged element was about to explode, prevent the explosion */
8564         ExplodeField[newx][newy] = EX_TYPE_NONE;
8565
8566         PlayLevelSoundAction(x, y, action);
8567       }
8568
8569       Store[newx][newy] = EL_EMPTY;
8570
8571 #if 1
8572       /* this makes it possible to leave the removed element again */
8573       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8574         Store[newx][newy] = new_element;
8575 #else
8576       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8577       {
8578         int move_leave_element = element_info[element].move_leave_element;
8579
8580         /* this makes it possible to leave the removed element again */
8581         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8582                              new_element : move_leave_element);
8583       }
8584 #endif
8585
8586 #endif
8587
8588       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8589       {
8590         RunnerVisit[x][y] = FrameCounter;
8591         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8592       }
8593     }
8594     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8595     {
8596       if (!IS_FREE(newx, newy))
8597       {
8598         if (IS_PLAYER(x, y))
8599           DrawPlayerField(x, y);
8600         else
8601           TEST_DrawLevelField(x, y);
8602
8603         return;
8604       }
8605       else
8606       {
8607         boolean wanna_flame = !RND(10);
8608         int dx = newx - x, dy = newy - y;
8609         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8610         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8611         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8612                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8613         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8614                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8615
8616         if ((wanna_flame ||
8617              IS_CLASSIC_ENEMY(element1) ||
8618              IS_CLASSIC_ENEMY(element2)) &&
8619             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8620             element1 != EL_FLAMES && element2 != EL_FLAMES)
8621         {
8622           ResetGfxAnimation(x, y);
8623           GfxAction[x][y] = ACTION_ATTACKING;
8624
8625           if (IS_PLAYER(x, y))
8626             DrawPlayerField(x, y);
8627           else
8628             TEST_DrawLevelField(x, y);
8629
8630           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8631
8632           MovDelay[x][y] = 50;
8633
8634           /* !!! */
8635 #if 0
8636           RemoveField(newx, newy);
8637 #endif
8638           Feld[newx][newy] = EL_FLAMES;
8639           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8640           {
8641 #if 0
8642             RemoveField(newx1, newy1);
8643 #endif
8644             Feld[newx1][newy1] = EL_FLAMES;
8645           }
8646           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8647           {
8648 #if 0
8649             RemoveField(newx2, newy2);
8650 #endif
8651             Feld[newx2][newy2] = EL_FLAMES;
8652           }
8653
8654           return;
8655         }
8656       }
8657     }
8658     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8659              Feld[newx][newy] == EL_DIAMOND)
8660     {
8661       if (IS_MOVING(newx, newy))
8662         RemoveMovingField(newx, newy);
8663       else
8664       {
8665         Feld[newx][newy] = EL_EMPTY;
8666         TEST_DrawLevelField(newx, newy);
8667       }
8668
8669       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8670     }
8671     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8672              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8673     {
8674       if (AmoebaNr[newx][newy])
8675       {
8676         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8677         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8678             Feld[newx][newy] == EL_BD_AMOEBA)
8679           AmoebaCnt[AmoebaNr[newx][newy]]--;
8680       }
8681
8682 #if 0
8683       /* !!! test !!! */
8684       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8685       {
8686         RemoveMovingField(newx, newy);
8687       }
8688 #else
8689       if (IS_MOVING(newx, newy))
8690       {
8691         RemoveMovingField(newx, newy);
8692       }
8693 #endif
8694       else
8695       {
8696         Feld[newx][newy] = EL_EMPTY;
8697         TEST_DrawLevelField(newx, newy);
8698       }
8699
8700       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8701     }
8702     else if ((element == EL_PACMAN || element == EL_MOLE)
8703              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8704     {
8705       if (AmoebaNr[newx][newy])
8706       {
8707         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8708         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8709             Feld[newx][newy] == EL_BD_AMOEBA)
8710           AmoebaCnt[AmoebaNr[newx][newy]]--;
8711       }
8712
8713       if (element == EL_MOLE)
8714       {
8715         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8716         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8717
8718         ResetGfxAnimation(x, y);
8719         GfxAction[x][y] = ACTION_DIGGING;
8720         TEST_DrawLevelField(x, y);
8721
8722         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8723
8724         return;                         /* wait for shrinking amoeba */
8725       }
8726       else      /* element == EL_PACMAN */
8727       {
8728         Feld[newx][newy] = EL_EMPTY;
8729         TEST_DrawLevelField(newx, newy);
8730         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8731       }
8732     }
8733     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8734              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8735               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8736     {
8737       /* wait for shrinking amoeba to completely disappear */
8738       return;
8739     }
8740     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8741     {
8742       /* object was running against a wall */
8743
8744       TurnRound(x, y);
8745
8746 #if 0
8747       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8748       if (move_pattern & MV_ANY_DIRECTION &&
8749           move_pattern == MovDir[x][y])
8750       {
8751         int blocking_element =
8752           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8753
8754         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8755                                  MovDir[x][y]);
8756
8757         element = Feld[x][y];   /* element might have changed */
8758       }
8759 #endif
8760
8761       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8762         DrawLevelElementAnimation(x, y, element);
8763
8764       if (DONT_TOUCH(element))
8765         TestIfBadThingTouchesPlayer(x, y);
8766
8767       return;
8768     }
8769
8770     InitMovingField(x, y, MovDir[x][y]);
8771
8772     PlayLevelSoundAction(x, y, ACTION_MOVING);
8773   }
8774
8775   if (MovDir[x][y])
8776     ContinueMoving(x, y);
8777 }
8778
8779 void ContinueMoving(int x, int y)
8780 {
8781   int element = Feld[x][y];
8782   struct ElementInfo *ei = &element_info[element];
8783   int direction = MovDir[x][y];
8784   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8785   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8786   int newx = x + dx, newy = y + dy;
8787   int stored = Store[x][y];
8788   int stored_new = Store[newx][newy];
8789   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8790   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8791   boolean last_line = (newy == lev_fieldy - 1);
8792
8793   MovPos[x][y] += getElementMoveStepsize(x, y);
8794
8795   if (pushed_by_player) /* special case: moving object pushed by player */
8796     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8797
8798   if (ABS(MovPos[x][y]) < TILEX)
8799   {
8800 #if 0
8801     int ee = Feld[x][y];
8802     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8803     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8804
8805     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8806            x, y, ABS(MovPos[x][y]),
8807            ee, gg, ff,
8808            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8809 #endif
8810
8811     TEST_DrawLevelField(x, y);
8812
8813     return;     /* element is still moving */
8814   }
8815
8816   /* element reached destination field */
8817
8818   Feld[x][y] = EL_EMPTY;
8819   Feld[newx][newy] = element;
8820   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8821
8822   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8823   {
8824     element = Feld[newx][newy] = EL_ACID;
8825   }
8826   else if (element == EL_MOLE)
8827   {
8828     Feld[x][y] = EL_SAND;
8829
8830     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8831   }
8832   else if (element == EL_QUICKSAND_FILLING)
8833   {
8834     element = Feld[newx][newy] = get_next_element(element);
8835     Store[newx][newy] = Store[x][y];
8836   }
8837   else if (element == EL_QUICKSAND_EMPTYING)
8838   {
8839     Feld[x][y] = get_next_element(element);
8840     element = Feld[newx][newy] = Store[x][y];
8841   }
8842   else if (element == EL_QUICKSAND_FAST_FILLING)
8843   {
8844     element = Feld[newx][newy] = get_next_element(element);
8845     Store[newx][newy] = Store[x][y];
8846   }
8847   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8848   {
8849     Feld[x][y] = get_next_element(element);
8850     element = Feld[newx][newy] = Store[x][y];
8851   }
8852   else if (element == EL_MAGIC_WALL_FILLING)
8853   {
8854     element = Feld[newx][newy] = get_next_element(element);
8855     if (!game.magic_wall_active)
8856       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8857     Store[newx][newy] = Store[x][y];
8858   }
8859   else if (element == EL_MAGIC_WALL_EMPTYING)
8860   {
8861     Feld[x][y] = get_next_element(element);
8862     if (!game.magic_wall_active)
8863       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8864     element = Feld[newx][newy] = Store[x][y];
8865
8866 #if USE_NEW_CUSTOM_VALUE
8867     InitField(newx, newy, FALSE);
8868 #endif
8869   }
8870   else if (element == EL_BD_MAGIC_WALL_FILLING)
8871   {
8872     element = Feld[newx][newy] = get_next_element(element);
8873     if (!game.magic_wall_active)
8874       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8875     Store[newx][newy] = Store[x][y];
8876   }
8877   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8878   {
8879     Feld[x][y] = get_next_element(element);
8880     if (!game.magic_wall_active)
8881       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8882     element = Feld[newx][newy] = Store[x][y];
8883
8884 #if USE_NEW_CUSTOM_VALUE
8885     InitField(newx, newy, FALSE);
8886 #endif
8887   }
8888   else if (element == EL_DC_MAGIC_WALL_FILLING)
8889   {
8890     element = Feld[newx][newy] = get_next_element(element);
8891     if (!game.magic_wall_active)
8892       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8893     Store[newx][newy] = Store[x][y];
8894   }
8895   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8896   {
8897     Feld[x][y] = get_next_element(element);
8898     if (!game.magic_wall_active)
8899       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8900     element = Feld[newx][newy] = Store[x][y];
8901
8902 #if USE_NEW_CUSTOM_VALUE
8903     InitField(newx, newy, FALSE);
8904 #endif
8905   }
8906   else if (element == EL_AMOEBA_DROPPING)
8907   {
8908     Feld[x][y] = get_next_element(element);
8909     element = Feld[newx][newy] = Store[x][y];
8910   }
8911   else if (element == EL_SOKOBAN_OBJECT)
8912   {
8913     if (Back[x][y])
8914       Feld[x][y] = Back[x][y];
8915
8916     if (Back[newx][newy])
8917       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8918
8919     Back[x][y] = Back[newx][newy] = 0;
8920   }
8921
8922   Store[x][y] = EL_EMPTY;
8923   MovPos[x][y] = 0;
8924   MovDir[x][y] = 0;
8925   MovDelay[x][y] = 0;
8926
8927   MovDelay[newx][newy] = 0;
8928
8929   if (CAN_CHANGE_OR_HAS_ACTION(element))
8930   {
8931     /* copy element change control values to new field */
8932     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8933     ChangePage[newx][newy]  = ChangePage[x][y];
8934     ChangeCount[newx][newy] = ChangeCount[x][y];
8935     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8936   }
8937
8938 #if USE_NEW_CUSTOM_VALUE
8939   CustomValue[newx][newy] = CustomValue[x][y];
8940 #endif
8941
8942   ChangeDelay[x][y] = 0;
8943   ChangePage[x][y] = -1;
8944   ChangeCount[x][y] = 0;
8945   ChangeEvent[x][y] = -1;
8946
8947 #if USE_NEW_CUSTOM_VALUE
8948   CustomValue[x][y] = 0;
8949 #endif
8950
8951   /* copy animation control values to new field */
8952   GfxFrame[newx][newy]  = GfxFrame[x][y];
8953   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8954   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8955   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8956
8957   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8958
8959   /* some elements can leave other elements behind after moving */
8960 #if 1
8961   if (ei->move_leave_element != EL_EMPTY &&
8962       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8963       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8964 #else
8965   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8966       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8967       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8968 #endif
8969   {
8970     int move_leave_element = ei->move_leave_element;
8971
8972 #if 1
8973 #if 1
8974     /* this makes it possible to leave the removed element again */
8975     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8976       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8977 #else
8978     /* this makes it possible to leave the removed element again */
8979     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8980       move_leave_element = stored;
8981 #endif
8982 #else
8983     /* this makes it possible to leave the removed element again */
8984     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8985         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8986       move_leave_element = stored;
8987 #endif
8988
8989     Feld[x][y] = move_leave_element;
8990
8991     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8992       MovDir[x][y] = direction;
8993
8994     InitField(x, y, FALSE);
8995
8996     if (GFX_CRUMBLED(Feld[x][y]))
8997       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8998
8999     if (ELEM_IS_PLAYER(move_leave_element))
9000       RelocatePlayer(x, y, move_leave_element);
9001   }
9002
9003   /* do this after checking for left-behind element */
9004   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9005
9006   if (!CAN_MOVE(element) ||
9007       (CAN_FALL(element) && direction == MV_DOWN &&
9008        (element == EL_SPRING ||
9009         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9010         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9011     GfxDir[x][y] = MovDir[newx][newy] = 0;
9012
9013   TEST_DrawLevelField(x, y);
9014   TEST_DrawLevelField(newx, newy);
9015
9016   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9017
9018   /* prevent pushed element from moving on in pushed direction */
9019   if (pushed_by_player && CAN_MOVE(element) &&
9020       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9021       !(element_info[element].move_pattern & direction))
9022     TurnRound(newx, newy);
9023
9024   /* prevent elements on conveyor belt from moving on in last direction */
9025   if (pushed_by_conveyor && CAN_FALL(element) &&
9026       direction & MV_HORIZONTAL)
9027     MovDir[newx][newy] = 0;
9028
9029   if (!pushed_by_player)
9030   {
9031     int nextx = newx + dx, nexty = newy + dy;
9032     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9033
9034     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9035
9036     if (CAN_FALL(element) && direction == MV_DOWN)
9037       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9038
9039     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9040       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9041
9042 #if USE_FIX_IMPACT_COLLISION
9043     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9044       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9045 #endif
9046   }
9047
9048   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9049   {
9050     TestIfBadThingTouchesPlayer(newx, newy);
9051     TestIfBadThingTouchesFriend(newx, newy);
9052
9053     if (!IS_CUSTOM_ELEMENT(element))
9054       TestIfBadThingTouchesOtherBadThing(newx, newy);
9055   }
9056   else if (element == EL_PENGUIN)
9057     TestIfFriendTouchesBadThing(newx, newy);
9058
9059   if (DONT_GET_HIT_BY(element))
9060   {
9061     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9062   }
9063
9064   /* give the player one last chance (one more frame) to move away */
9065   if (CAN_FALL(element) && direction == MV_DOWN &&
9066       (last_line || (!IS_FREE(x, newy + 1) &&
9067                      (!IS_PLAYER(x, newy + 1) ||
9068                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9069     Impact(x, newy);
9070
9071   if (pushed_by_player && !game.use_change_when_pushing_bug)
9072   {
9073     int push_side = MV_DIR_OPPOSITE(direction);
9074     struct PlayerInfo *player = PLAYERINFO(x, y);
9075
9076     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9077                                player->index_bit, push_side);
9078     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9079                                         player->index_bit, push_side);
9080   }
9081
9082   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9083     MovDelay[newx][newy] = 1;
9084
9085   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9086
9087   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9088
9089 #if 0
9090   if (ChangePage[newx][newy] != -1)             /* delayed change */
9091   {
9092     int page = ChangePage[newx][newy];
9093     struct ElementChangeInfo *change = &ei->change_page[page];
9094
9095     ChangePage[newx][newy] = -1;
9096
9097     if (change->can_change)
9098     {
9099       if (ChangeElement(newx, newy, element, page))
9100       {
9101         if (change->post_change_function)
9102           change->post_change_function(newx, newy);
9103       }
9104     }
9105
9106     if (change->has_action)
9107       ExecuteCustomElementAction(newx, newy, element, page);
9108   }
9109 #endif
9110
9111   TestIfElementHitsCustomElement(newx, newy, direction);
9112   TestIfPlayerTouchesCustomElement(newx, newy);
9113   TestIfElementTouchesCustomElement(newx, newy);
9114
9115   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9116       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9117     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9118                              MV_DIR_OPPOSITE(direction));
9119 }
9120
9121 int AmoebeNachbarNr(int ax, int ay)
9122 {
9123   int i;
9124   int element = Feld[ax][ay];
9125   int group_nr = 0;
9126   static int xy[4][2] =
9127   {
9128     { 0, -1 },
9129     { -1, 0 },
9130     { +1, 0 },
9131     { 0, +1 }
9132   };
9133
9134   for (i = 0; i < NUM_DIRECTIONS; i++)
9135   {
9136     int x = ax + xy[i][0];
9137     int y = ay + xy[i][1];
9138
9139     if (!IN_LEV_FIELD(x, y))
9140       continue;
9141
9142     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9143       group_nr = AmoebaNr[x][y];
9144   }
9145
9146   return group_nr;
9147 }
9148
9149 void AmoebenVereinigen(int ax, int ay)
9150 {
9151   int i, x, y, xx, yy;
9152   int new_group_nr = AmoebaNr[ax][ay];
9153   static int xy[4][2] =
9154   {
9155     { 0, -1 },
9156     { -1, 0 },
9157     { +1, 0 },
9158     { 0, +1 }
9159   };
9160
9161   if (new_group_nr == 0)
9162     return;
9163
9164   for (i = 0; i < NUM_DIRECTIONS; i++)
9165   {
9166     x = ax + xy[i][0];
9167     y = ay + xy[i][1];
9168
9169     if (!IN_LEV_FIELD(x, y))
9170       continue;
9171
9172     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9173          Feld[x][y] == EL_BD_AMOEBA ||
9174          Feld[x][y] == EL_AMOEBA_DEAD) &&
9175         AmoebaNr[x][y] != new_group_nr)
9176     {
9177       int old_group_nr = AmoebaNr[x][y];
9178
9179       if (old_group_nr == 0)
9180         return;
9181
9182       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9183       AmoebaCnt[old_group_nr] = 0;
9184       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9185       AmoebaCnt2[old_group_nr] = 0;
9186
9187       SCAN_PLAYFIELD(xx, yy)
9188       {
9189         if (AmoebaNr[xx][yy] == old_group_nr)
9190           AmoebaNr[xx][yy] = new_group_nr;
9191       }
9192     }
9193   }
9194 }
9195
9196 void AmoebeUmwandeln(int ax, int ay)
9197 {
9198   int i, x, y;
9199
9200   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9201   {
9202     int group_nr = AmoebaNr[ax][ay];
9203
9204 #ifdef DEBUG
9205     if (group_nr == 0)
9206     {
9207       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9208       printf("AmoebeUmwandeln(): This should never happen!\n");
9209       return;
9210     }
9211 #endif
9212
9213     SCAN_PLAYFIELD(x, y)
9214     {
9215       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9216       {
9217         AmoebaNr[x][y] = 0;
9218         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9219       }
9220     }
9221
9222     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9223                             SND_AMOEBA_TURNING_TO_GEM :
9224                             SND_AMOEBA_TURNING_TO_ROCK));
9225     Bang(ax, ay);
9226   }
9227   else
9228   {
9229     static int xy[4][2] =
9230     {
9231       { 0, -1 },
9232       { -1, 0 },
9233       { +1, 0 },
9234       { 0, +1 }
9235     };
9236
9237     for (i = 0; i < NUM_DIRECTIONS; i++)
9238     {
9239       x = ax + xy[i][0];
9240       y = ay + xy[i][1];
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9246       {
9247         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9248                               SND_AMOEBA_TURNING_TO_GEM :
9249                               SND_AMOEBA_TURNING_TO_ROCK));
9250         Bang(x, y);
9251       }
9252     }
9253   }
9254 }
9255
9256 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9257 {
9258   int x, y;
9259   int group_nr = AmoebaNr[ax][ay];
9260   boolean done = FALSE;
9261
9262 #ifdef DEBUG
9263   if (group_nr == 0)
9264   {
9265     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9266     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9267     return;
9268   }
9269 #endif
9270
9271   SCAN_PLAYFIELD(x, y)
9272   {
9273     if (AmoebaNr[x][y] == group_nr &&
9274         (Feld[x][y] == EL_AMOEBA_DEAD ||
9275          Feld[x][y] == EL_BD_AMOEBA ||
9276          Feld[x][y] == EL_AMOEBA_GROWING))
9277     {
9278       AmoebaNr[x][y] = 0;
9279       Feld[x][y] = new_element;
9280       InitField(x, y, FALSE);
9281       TEST_DrawLevelField(x, y);
9282       done = TRUE;
9283     }
9284   }
9285
9286   if (done)
9287     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9288                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9289                             SND_BD_AMOEBA_TURNING_TO_GEM));
9290 }
9291
9292 void AmoebeWaechst(int x, int y)
9293 {
9294   static unsigned long sound_delay = 0;
9295   static unsigned long sound_delay_value = 0;
9296
9297   if (!MovDelay[x][y])          /* start new growing cycle */
9298   {
9299     MovDelay[x][y] = 7;
9300
9301     if (DelayReached(&sound_delay, sound_delay_value))
9302     {
9303       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9304       sound_delay_value = 30;
9305     }
9306   }
9307
9308   if (MovDelay[x][y])           /* wait some time before growing bigger */
9309   {
9310     MovDelay[x][y]--;
9311     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9312     {
9313       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9314                                            6 - MovDelay[x][y]);
9315
9316       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9317     }
9318
9319     if (!MovDelay[x][y])
9320     {
9321       Feld[x][y] = Store[x][y];
9322       Store[x][y] = 0;
9323       TEST_DrawLevelField(x, y);
9324     }
9325   }
9326 }
9327
9328 void AmoebaDisappearing(int x, int y)
9329 {
9330   static unsigned long sound_delay = 0;
9331   static unsigned long sound_delay_value = 0;
9332
9333   if (!MovDelay[x][y])          /* start new shrinking cycle */
9334   {
9335     MovDelay[x][y] = 7;
9336
9337     if (DelayReached(&sound_delay, sound_delay_value))
9338       sound_delay_value = 30;
9339   }
9340
9341   if (MovDelay[x][y])           /* wait some time before shrinking */
9342   {
9343     MovDelay[x][y]--;
9344     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9345     {
9346       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9347                                            6 - MovDelay[x][y]);
9348
9349       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9350     }
9351
9352     if (!MovDelay[x][y])
9353     {
9354       Feld[x][y] = EL_EMPTY;
9355       TEST_DrawLevelField(x, y);
9356
9357       /* don't let mole enter this field in this cycle;
9358          (give priority to objects falling to this field from above) */
9359       Stop[x][y] = TRUE;
9360     }
9361   }
9362 }
9363
9364 void AmoebeAbleger(int ax, int ay)
9365 {
9366   int i;
9367   int element = Feld[ax][ay];
9368   int graphic = el2img(element);
9369   int newax = ax, neway = ay;
9370   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9371   static int xy[4][2] =
9372   {
9373     { 0, -1 },
9374     { -1, 0 },
9375     { +1, 0 },
9376     { 0, +1 }
9377   };
9378
9379   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9380   {
9381     Feld[ax][ay] = EL_AMOEBA_DEAD;
9382     TEST_DrawLevelField(ax, ay);
9383     return;
9384   }
9385
9386   if (IS_ANIMATED(graphic))
9387     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9388
9389   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9390     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9391
9392   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9393   {
9394     MovDelay[ax][ay]--;
9395     if (MovDelay[ax][ay])
9396       return;
9397   }
9398
9399   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9400   {
9401     int start = RND(4);
9402     int x = ax + xy[start][0];
9403     int y = ay + xy[start][1];
9404
9405     if (!IN_LEV_FIELD(x, y))
9406       return;
9407
9408     if (IS_FREE(x, y) ||
9409         CAN_GROW_INTO(Feld[x][y]) ||
9410         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9411         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9412     {
9413       newax = x;
9414       neway = y;
9415     }
9416
9417     if (newax == ax && neway == ay)
9418       return;
9419   }
9420   else                          /* normal or "filled" (BD style) amoeba */
9421   {
9422     int start = RND(4);
9423     boolean waiting_for_player = FALSE;
9424
9425     for (i = 0; i < NUM_DIRECTIONS; i++)
9426     {
9427       int j = (start + i) % 4;
9428       int x = ax + xy[j][0];
9429       int y = ay + xy[j][1];
9430
9431       if (!IN_LEV_FIELD(x, y))
9432         continue;
9433
9434       if (IS_FREE(x, y) ||
9435           CAN_GROW_INTO(Feld[x][y]) ||
9436           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9437           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9438       {
9439         newax = x;
9440         neway = y;
9441         break;
9442       }
9443       else if (IS_PLAYER(x, y))
9444         waiting_for_player = TRUE;
9445     }
9446
9447     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9448     {
9449       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9450       {
9451         Feld[ax][ay] = EL_AMOEBA_DEAD;
9452         TEST_DrawLevelField(ax, ay);
9453         AmoebaCnt[AmoebaNr[ax][ay]]--;
9454
9455         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9456         {
9457           if (element == EL_AMOEBA_FULL)
9458             AmoebeUmwandeln(ax, ay);
9459           else if (element == EL_BD_AMOEBA)
9460             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9461         }
9462       }
9463       return;
9464     }
9465     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9466     {
9467       /* amoeba gets larger by growing in some direction */
9468
9469       int new_group_nr = AmoebaNr[ax][ay];
9470
9471 #ifdef DEBUG
9472   if (new_group_nr == 0)
9473   {
9474     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9475     printf("AmoebeAbleger(): This should never happen!\n");
9476     return;
9477   }
9478 #endif
9479
9480       AmoebaNr[newax][neway] = new_group_nr;
9481       AmoebaCnt[new_group_nr]++;
9482       AmoebaCnt2[new_group_nr]++;
9483
9484       /* if amoeba touches other amoeba(s) after growing, unify them */
9485       AmoebenVereinigen(newax, neway);
9486
9487       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9488       {
9489         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9490         return;
9491       }
9492     }
9493   }
9494
9495   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9496       (neway == lev_fieldy - 1 && newax != ax))
9497   {
9498     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9499     Store[newax][neway] = element;
9500   }
9501   else if (neway == ay || element == EL_EMC_DRIPPER)
9502   {
9503     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9504
9505     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9506   }
9507   else
9508   {
9509     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9510     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9511     Store[ax][ay] = EL_AMOEBA_DROP;
9512     ContinueMoving(ax, ay);
9513     return;
9514   }
9515
9516   TEST_DrawLevelField(newax, neway);
9517 }
9518
9519 void Life(int ax, int ay)
9520 {
9521   int x1, y1, x2, y2;
9522   int life_time = 40;
9523   int element = Feld[ax][ay];
9524   int graphic = el2img(element);
9525   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9526                          level.biomaze);
9527   boolean changed = FALSE;
9528
9529   if (IS_ANIMATED(graphic))
9530     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9531
9532   if (Stop[ax][ay])
9533     return;
9534
9535   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9536     MovDelay[ax][ay] = life_time;
9537
9538   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9539   {
9540     MovDelay[ax][ay]--;
9541     if (MovDelay[ax][ay])
9542       return;
9543   }
9544
9545   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9546   {
9547     int xx = ax+x1, yy = ay+y1;
9548     int nachbarn = 0;
9549
9550     if (!IN_LEV_FIELD(xx, yy))
9551       continue;
9552
9553     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9554     {
9555       int x = xx+x2, y = yy+y2;
9556
9557       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9558         continue;
9559
9560       if (((Feld[x][y] == element ||
9561             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9562            !Stop[x][y]) ||
9563           (IS_FREE(x, y) && Stop[x][y]))
9564         nachbarn++;
9565     }
9566
9567     if (xx == ax && yy == ay)           /* field in the middle */
9568     {
9569       if (nachbarn < life_parameter[0] ||
9570           nachbarn > life_parameter[1])
9571       {
9572         Feld[xx][yy] = EL_EMPTY;
9573         if (!Stop[xx][yy])
9574           TEST_DrawLevelField(xx, yy);
9575         Stop[xx][yy] = TRUE;
9576         changed = TRUE;
9577       }
9578     }
9579     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9580     {                                   /* free border field */
9581       if (nachbarn >= life_parameter[2] &&
9582           nachbarn <= life_parameter[3])
9583       {
9584         Feld[xx][yy] = element;
9585         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9586         if (!Stop[xx][yy])
9587           TEST_DrawLevelField(xx, yy);
9588         Stop[xx][yy] = TRUE;
9589         changed = TRUE;
9590       }
9591     }
9592   }
9593
9594   if (changed)
9595     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9596                    SND_GAME_OF_LIFE_GROWING);
9597 }
9598
9599 static void InitRobotWheel(int x, int y)
9600 {
9601   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9602 }
9603
9604 static void RunRobotWheel(int x, int y)
9605 {
9606   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9607 }
9608
9609 static void StopRobotWheel(int x, int y)
9610 {
9611   if (ZX == x && ZY == y)
9612   {
9613     ZX = ZY = -1;
9614
9615     game.robot_wheel_active = FALSE;
9616   }
9617 }
9618
9619 static void InitTimegateWheel(int x, int y)
9620 {
9621   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9622 }
9623
9624 static void RunTimegateWheel(int x, int y)
9625 {
9626   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9627 }
9628
9629 static void InitMagicBallDelay(int x, int y)
9630 {
9631 #if 1
9632   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9633 #else
9634   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9635 #endif
9636 }
9637
9638 static void ActivateMagicBall(int bx, int by)
9639 {
9640   int x, y;
9641
9642   if (level.ball_random)
9643   {
9644     int pos_border = RND(8);    /* select one of the eight border elements */
9645     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9646     int xx = pos_content % 3;
9647     int yy = pos_content / 3;
9648
9649     x = bx - 1 + xx;
9650     y = by - 1 + yy;
9651
9652     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9653       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9654   }
9655   else
9656   {
9657     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9658     {
9659       int xx = x - bx + 1;
9660       int yy = y - by + 1;
9661
9662       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9663         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9664     }
9665   }
9666
9667   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9668 }
9669
9670 void CheckExit(int x, int y)
9671 {
9672   if (local_player->gems_still_needed > 0 ||
9673       local_player->sokobanfields_still_needed > 0 ||
9674       local_player->lights_still_needed > 0)
9675   {
9676     int element = Feld[x][y];
9677     int graphic = el2img(element);
9678
9679     if (IS_ANIMATED(graphic))
9680       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9681
9682     return;
9683   }
9684
9685   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9686     return;
9687
9688   Feld[x][y] = EL_EXIT_OPENING;
9689
9690   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9691 }
9692
9693 void CheckExitEM(int x, int y)
9694 {
9695   if (local_player->gems_still_needed > 0 ||
9696       local_player->sokobanfields_still_needed > 0 ||
9697       local_player->lights_still_needed > 0)
9698   {
9699     int element = Feld[x][y];
9700     int graphic = el2img(element);
9701
9702     if (IS_ANIMATED(graphic))
9703       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9704
9705     return;
9706   }
9707
9708   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9709     return;
9710
9711   Feld[x][y] = EL_EM_EXIT_OPENING;
9712
9713   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9714 }
9715
9716 void CheckExitSteel(int x, int y)
9717 {
9718   if (local_player->gems_still_needed > 0 ||
9719       local_player->sokobanfields_still_needed > 0 ||
9720       local_player->lights_still_needed > 0)
9721   {
9722     int element = Feld[x][y];
9723     int graphic = el2img(element);
9724
9725     if (IS_ANIMATED(graphic))
9726       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9727
9728     return;
9729   }
9730
9731   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9732     return;
9733
9734   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9735
9736   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9737 }
9738
9739 void CheckExitSteelEM(int x, int y)
9740 {
9741   if (local_player->gems_still_needed > 0 ||
9742       local_player->sokobanfields_still_needed > 0 ||
9743       local_player->lights_still_needed > 0)
9744   {
9745     int element = Feld[x][y];
9746     int graphic = el2img(element);
9747
9748     if (IS_ANIMATED(graphic))
9749       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9750
9751     return;
9752   }
9753
9754   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9755     return;
9756
9757   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9758
9759   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9760 }
9761
9762 void CheckExitSP(int x, int y)
9763 {
9764   if (local_player->gems_still_needed > 0)
9765   {
9766     int element = Feld[x][y];
9767     int graphic = el2img(element);
9768
9769     if (IS_ANIMATED(graphic))
9770       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9771
9772     return;
9773   }
9774
9775   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9776     return;
9777
9778   Feld[x][y] = EL_SP_EXIT_OPENING;
9779
9780   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9781 }
9782
9783 static void CloseAllOpenTimegates()
9784 {
9785   int x, y;
9786
9787   SCAN_PLAYFIELD(x, y)
9788   {
9789     int element = Feld[x][y];
9790
9791     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9792     {
9793       Feld[x][y] = EL_TIMEGATE_CLOSING;
9794
9795       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9796     }
9797   }
9798 }
9799
9800 void DrawTwinkleOnField(int x, int y)
9801 {
9802   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9803     return;
9804
9805   if (Feld[x][y] == EL_BD_DIAMOND)
9806     return;
9807
9808   if (MovDelay[x][y] == 0)      /* next animation frame */
9809     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9810
9811   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9812   {
9813     MovDelay[x][y]--;
9814
9815     DrawLevelElementAnimation(x, y, Feld[x][y]);
9816
9817     if (MovDelay[x][y] != 0)
9818     {
9819       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9820                                            10 - MovDelay[x][y]);
9821
9822       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9823     }
9824   }
9825 }
9826
9827 void MauerWaechst(int x, int y)
9828 {
9829   int delay = 6;
9830
9831   if (!MovDelay[x][y])          /* next animation frame */
9832     MovDelay[x][y] = 3 * delay;
9833
9834   if (MovDelay[x][y])           /* wait some time before next frame */
9835   {
9836     MovDelay[x][y]--;
9837
9838     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9839     {
9840       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9841       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9842
9843       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9844     }
9845
9846     if (!MovDelay[x][y])
9847     {
9848       if (MovDir[x][y] == MV_LEFT)
9849       {
9850         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9851           TEST_DrawLevelField(x - 1, y);
9852       }
9853       else if (MovDir[x][y] == MV_RIGHT)
9854       {
9855         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9856           TEST_DrawLevelField(x + 1, y);
9857       }
9858       else if (MovDir[x][y] == MV_UP)
9859       {
9860         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9861           TEST_DrawLevelField(x, y - 1);
9862       }
9863       else
9864       {
9865         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9866           TEST_DrawLevelField(x, y + 1);
9867       }
9868
9869       Feld[x][y] = Store[x][y];
9870       Store[x][y] = 0;
9871       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9872       TEST_DrawLevelField(x, y);
9873     }
9874   }
9875 }
9876
9877 void MauerAbleger(int ax, int ay)
9878 {
9879   int element = Feld[ax][ay];
9880   int graphic = el2img(element);
9881   boolean oben_frei = FALSE, unten_frei = FALSE;
9882   boolean links_frei = FALSE, rechts_frei = FALSE;
9883   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9884   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9885   boolean new_wall = FALSE;
9886
9887   if (IS_ANIMATED(graphic))
9888     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9889
9890   if (!MovDelay[ax][ay])        /* start building new wall */
9891     MovDelay[ax][ay] = 6;
9892
9893   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9894   {
9895     MovDelay[ax][ay]--;
9896     if (MovDelay[ax][ay])
9897       return;
9898   }
9899
9900   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9901     oben_frei = TRUE;
9902   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9903     unten_frei = TRUE;
9904   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9905     links_frei = TRUE;
9906   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9907     rechts_frei = TRUE;
9908
9909   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9910       element == EL_EXPANDABLE_WALL_ANY)
9911   {
9912     if (oben_frei)
9913     {
9914       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9915       Store[ax][ay-1] = element;
9916       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9917       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9918         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9919                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9920       new_wall = TRUE;
9921     }
9922     if (unten_frei)
9923     {
9924       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9925       Store[ax][ay+1] = element;
9926       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9927       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9928         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9929                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9930       new_wall = TRUE;
9931     }
9932   }
9933
9934   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9935       element == EL_EXPANDABLE_WALL_ANY ||
9936       element == EL_EXPANDABLE_WALL ||
9937       element == EL_BD_EXPANDABLE_WALL)
9938   {
9939     if (links_frei)
9940     {
9941       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9942       Store[ax-1][ay] = element;
9943       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9944       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9945         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9946                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9947       new_wall = TRUE;
9948     }
9949
9950     if (rechts_frei)
9951     {
9952       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9953       Store[ax+1][ay] = element;
9954       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9955       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9956         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9957                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9958       new_wall = TRUE;
9959     }
9960   }
9961
9962   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9963     TEST_DrawLevelField(ax, ay);
9964
9965   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9966     oben_massiv = TRUE;
9967   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9968     unten_massiv = TRUE;
9969   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9970     links_massiv = TRUE;
9971   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9972     rechts_massiv = TRUE;
9973
9974   if (((oben_massiv && unten_massiv) ||
9975        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9976        element == EL_EXPANDABLE_WALL) &&
9977       ((links_massiv && rechts_massiv) ||
9978        element == EL_EXPANDABLE_WALL_VERTICAL))
9979     Feld[ax][ay] = EL_WALL;
9980
9981   if (new_wall)
9982     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9983 }
9984
9985 void MauerAblegerStahl(int ax, int ay)
9986 {
9987   int element = Feld[ax][ay];
9988   int graphic = el2img(element);
9989   boolean oben_frei = FALSE, unten_frei = FALSE;
9990   boolean links_frei = FALSE, rechts_frei = FALSE;
9991   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9992   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9993   boolean new_wall = FALSE;
9994
9995   if (IS_ANIMATED(graphic))
9996     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9997
9998   if (!MovDelay[ax][ay])        /* start building new wall */
9999     MovDelay[ax][ay] = 6;
10000
10001   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10002   {
10003     MovDelay[ax][ay]--;
10004     if (MovDelay[ax][ay])
10005       return;
10006   }
10007
10008   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10009     oben_frei = TRUE;
10010   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10011     unten_frei = TRUE;
10012   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10013     links_frei = TRUE;
10014   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10015     rechts_frei = TRUE;
10016
10017   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10018       element == EL_EXPANDABLE_STEELWALL_ANY)
10019   {
10020     if (oben_frei)
10021     {
10022       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10023       Store[ax][ay-1] = element;
10024       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10025       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10026         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10027                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10028       new_wall = TRUE;
10029     }
10030     if (unten_frei)
10031     {
10032       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10033       Store[ax][ay+1] = element;
10034       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10035       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10036         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10037                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10038       new_wall = TRUE;
10039     }
10040   }
10041
10042   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10043       element == EL_EXPANDABLE_STEELWALL_ANY)
10044   {
10045     if (links_frei)
10046     {
10047       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10048       Store[ax-1][ay] = element;
10049       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10050       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10051         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10052                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10053       new_wall = TRUE;
10054     }
10055
10056     if (rechts_frei)
10057     {
10058       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10059       Store[ax+1][ay] = element;
10060       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10061       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10062         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10063                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10064       new_wall = TRUE;
10065     }
10066   }
10067
10068   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10069     oben_massiv = TRUE;
10070   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10071     unten_massiv = TRUE;
10072   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10073     links_massiv = TRUE;
10074   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10075     rechts_massiv = TRUE;
10076
10077   if (((oben_massiv && unten_massiv) ||
10078        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10079       ((links_massiv && rechts_massiv) ||
10080        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10081     Feld[ax][ay] = EL_STEELWALL;
10082
10083   if (new_wall)
10084     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10085 }
10086
10087 void CheckForDragon(int x, int y)
10088 {
10089   int i, j;
10090   boolean dragon_found = FALSE;
10091   static int xy[4][2] =
10092   {
10093     { 0, -1 },
10094     { -1, 0 },
10095     { +1, 0 },
10096     { 0, +1 }
10097   };
10098
10099   for (i = 0; i < NUM_DIRECTIONS; i++)
10100   {
10101     for (j = 0; j < 4; j++)
10102     {
10103       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10104
10105       if (IN_LEV_FIELD(xx, yy) &&
10106           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10107       {
10108         if (Feld[xx][yy] == EL_DRAGON)
10109           dragon_found = TRUE;
10110       }
10111       else
10112         break;
10113     }
10114   }
10115
10116   if (!dragon_found)
10117   {
10118     for (i = 0; i < NUM_DIRECTIONS; i++)
10119     {
10120       for (j = 0; j < 3; j++)
10121       {
10122         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10123   
10124         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10125         {
10126           Feld[xx][yy] = EL_EMPTY;
10127           TEST_DrawLevelField(xx, yy);
10128         }
10129         else
10130           break;
10131       }
10132     }
10133   }
10134 }
10135
10136 static void InitBuggyBase(int x, int y)
10137 {
10138   int element = Feld[x][y];
10139   int activating_delay = FRAMES_PER_SECOND / 4;
10140
10141   ChangeDelay[x][y] =
10142     (element == EL_SP_BUGGY_BASE ?
10143      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10144      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10145      activating_delay :
10146      element == EL_SP_BUGGY_BASE_ACTIVE ?
10147      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10148 }
10149
10150 static void WarnBuggyBase(int x, int y)
10151 {
10152   int i;
10153   static int xy[4][2] =
10154   {
10155     { 0, -1 },
10156     { -1, 0 },
10157     { +1, 0 },
10158     { 0, +1 }
10159   };
10160
10161   for (i = 0; i < NUM_DIRECTIONS; i++)
10162   {
10163     int xx = x + xy[i][0];
10164     int yy = y + xy[i][1];
10165
10166     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10167     {
10168       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10169
10170       break;
10171     }
10172   }
10173 }
10174
10175 static void InitTrap(int x, int y)
10176 {
10177   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10178 }
10179
10180 static void ActivateTrap(int x, int y)
10181 {
10182   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10183 }
10184
10185 static void ChangeActiveTrap(int x, int y)
10186 {
10187   int graphic = IMG_TRAP_ACTIVE;
10188
10189   /* if new animation frame was drawn, correct crumbled sand border */
10190   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10191     TEST_DrawLevelFieldCrumbledSand(x, y);
10192 }
10193
10194 static int getSpecialActionElement(int element, int number, int base_element)
10195 {
10196   return (element != EL_EMPTY ? element :
10197           number != -1 ? base_element + number - 1 :
10198           EL_EMPTY);
10199 }
10200
10201 static int getModifiedActionNumber(int value_old, int operator, int operand,
10202                                    int value_min, int value_max)
10203 {
10204   int value_new = (operator == CA_MODE_SET      ? operand :
10205                    operator == CA_MODE_ADD      ? value_old + operand :
10206                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10207                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10208                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10209                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10210                    value_old);
10211
10212   return (value_new < value_min ? value_min :
10213           value_new > value_max ? value_max :
10214           value_new);
10215 }
10216
10217 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10218 {
10219   struct ElementInfo *ei = &element_info[element];
10220   struct ElementChangeInfo *change = &ei->change_page[page];
10221   int target_element = change->target_element;
10222   int action_type = change->action_type;
10223   int action_mode = change->action_mode;
10224   int action_arg = change->action_arg;
10225   int action_element = change->action_element;
10226   int i;
10227
10228   if (!change->has_action)
10229     return;
10230
10231   /* ---------- determine action paramater values -------------------------- */
10232
10233   int level_time_value =
10234     (level.time > 0 ? TimeLeft :
10235      TimePlayed);
10236
10237   int action_arg_element_raw =
10238     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10239      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10240      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10241      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10242      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10243      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10244      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10245      EL_EMPTY);
10246   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10247
10248 #if 0
10249   if (action_arg_element_raw == EL_GROUP_START)
10250     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10251 #endif
10252
10253   int action_arg_direction =
10254     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10255      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10256      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10257      change->actual_trigger_side :
10258      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10259      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10260      MV_NONE);
10261
10262   int action_arg_number_min =
10263     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10264      CA_ARG_MIN);
10265
10266   int action_arg_number_max =
10267     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10268      action_type == CA_SET_LEVEL_GEMS ? 999 :
10269      action_type == CA_SET_LEVEL_TIME ? 9999 :
10270      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10271      action_type == CA_SET_CE_VALUE ? 9999 :
10272      action_type == CA_SET_CE_SCORE ? 9999 :
10273      CA_ARG_MAX);
10274
10275   int action_arg_number_reset =
10276     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10277      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10278      action_type == CA_SET_LEVEL_TIME ? level.time :
10279      action_type == CA_SET_LEVEL_SCORE ? 0 :
10280 #if USE_NEW_CUSTOM_VALUE
10281      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10282 #else
10283      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10284 #endif
10285      action_type == CA_SET_CE_SCORE ? 0 :
10286      0);
10287
10288   int action_arg_number =
10289     (action_arg <= CA_ARG_MAX ? action_arg :
10290      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10291      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10292      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10293      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10294      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10295      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10296 #if USE_NEW_CUSTOM_VALUE
10297      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10298 #else
10299      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10300 #endif
10301      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10302      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10303      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10304      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10305      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10306      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10307      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10308      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10309      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10310      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10311      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10312      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10313      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10314      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10315      -1);
10316
10317   int action_arg_number_old =
10318     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10319      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10320      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10321      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10322      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10323      0);
10324
10325   int action_arg_number_new =
10326     getModifiedActionNumber(action_arg_number_old,
10327                             action_mode, action_arg_number,
10328                             action_arg_number_min, action_arg_number_max);
10329
10330 #if 1
10331   int trigger_player_bits =
10332     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10333      change->actual_trigger_player_bits : change->trigger_player);
10334 #else
10335   int trigger_player_bits =
10336     (change->actual_trigger_player >= EL_PLAYER_1 &&
10337      change->actual_trigger_player <= EL_PLAYER_4 ?
10338      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10339      PLAYER_BITS_ANY);
10340 #endif
10341
10342   int action_arg_player_bits =
10343     (action_arg >= CA_ARG_PLAYER_1 &&
10344      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10345      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10346      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10347      PLAYER_BITS_ANY);
10348
10349   /* ---------- execute action  -------------------------------------------- */
10350
10351   switch (action_type)
10352   {
10353     case CA_NO_ACTION:
10354     {
10355       return;
10356     }
10357
10358     /* ---------- level actions  ------------------------------------------- */
10359
10360     case CA_RESTART_LEVEL:
10361     {
10362       game.restart_level = TRUE;
10363
10364       break;
10365     }
10366
10367     case CA_SHOW_ENVELOPE:
10368     {
10369       int element = getSpecialActionElement(action_arg_element,
10370                                             action_arg_number, EL_ENVELOPE_1);
10371
10372       if (IS_ENVELOPE(element))
10373         local_player->show_envelope = element;
10374
10375       break;
10376     }
10377
10378     case CA_SET_LEVEL_TIME:
10379     {
10380       if (level.time > 0)       /* only modify limited time value */
10381       {
10382         TimeLeft = action_arg_number_new;
10383
10384 #if 1
10385         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10386
10387         DisplayGameControlValues();
10388 #else
10389         DrawGameValue_Time(TimeLeft);
10390 #endif
10391
10392         if (!TimeLeft && setup.time_limit)
10393           for (i = 0; i < MAX_PLAYERS; i++)
10394             KillPlayer(&stored_player[i]);
10395       }
10396
10397       break;
10398     }
10399
10400     case CA_SET_LEVEL_SCORE:
10401     {
10402       local_player->score = action_arg_number_new;
10403
10404 #if 1
10405       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10406
10407       DisplayGameControlValues();
10408 #else
10409       DrawGameValue_Score(local_player->score);
10410 #endif
10411
10412       break;
10413     }
10414
10415     case CA_SET_LEVEL_GEMS:
10416     {
10417       local_player->gems_still_needed = action_arg_number_new;
10418
10419 #if 1
10420       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10421
10422       DisplayGameControlValues();
10423 #else
10424       DrawGameValue_Emeralds(local_player->gems_still_needed);
10425 #endif
10426
10427       break;
10428     }
10429
10430 #if !USE_PLAYER_GRAVITY
10431     case CA_SET_LEVEL_GRAVITY:
10432     {
10433       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10434                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10435                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10436                       game.gravity);
10437       break;
10438     }
10439 #endif
10440
10441     case CA_SET_LEVEL_WIND:
10442     {
10443       game.wind_direction = action_arg_direction;
10444
10445       break;
10446     }
10447
10448     case CA_SET_LEVEL_RANDOM_SEED:
10449     {
10450 #if 1
10451       /* ensure that setting a new random seed while playing is predictable */
10452       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10453 #else
10454       InitRND(action_arg_number_new);
10455 #endif
10456
10457 #if 0
10458       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10459 #endif
10460
10461 #if 0
10462       {
10463         int i;
10464
10465         printf("::: ");
10466         for (i = 0; i < 9; i++)
10467           printf("%d, ", RND(2));
10468         printf("\n");
10469       }
10470 #endif
10471
10472       break;
10473     }
10474
10475     /* ---------- player actions  ------------------------------------------ */
10476
10477     case CA_MOVE_PLAYER:
10478     {
10479       /* automatically move to the next field in specified direction */
10480       for (i = 0; i < MAX_PLAYERS; i++)
10481         if (trigger_player_bits & (1 << i))
10482           stored_player[i].programmed_action = action_arg_direction;
10483
10484       break;
10485     }
10486
10487     case CA_EXIT_PLAYER:
10488     {
10489       for (i = 0; i < MAX_PLAYERS; i++)
10490         if (action_arg_player_bits & (1 << i))
10491           PlayerWins(&stored_player[i]);
10492
10493       break;
10494     }
10495
10496     case CA_KILL_PLAYER:
10497     {
10498       for (i = 0; i < MAX_PLAYERS; i++)
10499         if (action_arg_player_bits & (1 << i))
10500           KillPlayer(&stored_player[i]);
10501
10502       break;
10503     }
10504
10505     case CA_SET_PLAYER_KEYS:
10506     {
10507       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10508       int element = getSpecialActionElement(action_arg_element,
10509                                             action_arg_number, EL_KEY_1);
10510
10511       if (IS_KEY(element))
10512       {
10513         for (i = 0; i < MAX_PLAYERS; i++)
10514         {
10515           if (trigger_player_bits & (1 << i))
10516           {
10517             stored_player[i].key[KEY_NR(element)] = key_state;
10518
10519             DrawGameDoorValues();
10520           }
10521         }
10522       }
10523
10524       break;
10525     }
10526
10527     case CA_SET_PLAYER_SPEED:
10528     {
10529 #if 0
10530       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10531 #endif
10532
10533       for (i = 0; i < MAX_PLAYERS; i++)
10534       {
10535         if (trigger_player_bits & (1 << i))
10536         {
10537           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10538
10539           if (action_arg == CA_ARG_SPEED_FASTER &&
10540               stored_player[i].cannot_move)
10541           {
10542             action_arg_number = STEPSIZE_VERY_SLOW;
10543           }
10544           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10545                    action_arg == CA_ARG_SPEED_FASTER)
10546           {
10547             action_arg_number = 2;
10548             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10549                            CA_MODE_MULTIPLY);
10550           }
10551           else if (action_arg == CA_ARG_NUMBER_RESET)
10552           {
10553             action_arg_number = level.initial_player_stepsize[i];
10554           }
10555
10556           move_stepsize =
10557             getModifiedActionNumber(move_stepsize,
10558                                     action_mode,
10559                                     action_arg_number,
10560                                     action_arg_number_min,
10561                                     action_arg_number_max);
10562
10563           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10564         }
10565       }
10566
10567       break;
10568     }
10569
10570     case CA_SET_PLAYER_SHIELD:
10571     {
10572       for (i = 0; i < MAX_PLAYERS; i++)
10573       {
10574         if (trigger_player_bits & (1 << i))
10575         {
10576           if (action_arg == CA_ARG_SHIELD_OFF)
10577           {
10578             stored_player[i].shield_normal_time_left = 0;
10579             stored_player[i].shield_deadly_time_left = 0;
10580           }
10581           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10582           {
10583             stored_player[i].shield_normal_time_left = 999999;
10584           }
10585           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10586           {
10587             stored_player[i].shield_normal_time_left = 999999;
10588             stored_player[i].shield_deadly_time_left = 999999;
10589           }
10590         }
10591       }
10592
10593       break;
10594     }
10595
10596 #if USE_PLAYER_GRAVITY
10597     case CA_SET_PLAYER_GRAVITY:
10598     {
10599       for (i = 0; i < MAX_PLAYERS; i++)
10600       {
10601         if (trigger_player_bits & (1 << i))
10602         {
10603           stored_player[i].gravity =
10604             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10605              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10606              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10607              stored_player[i].gravity);
10608         }
10609       }
10610
10611       break;
10612     }
10613 #endif
10614
10615     case CA_SET_PLAYER_ARTWORK:
10616     {
10617       for (i = 0; i < MAX_PLAYERS; i++)
10618       {
10619         if (trigger_player_bits & (1 << i))
10620         {
10621           int artwork_element = action_arg_element;
10622
10623           if (action_arg == CA_ARG_ELEMENT_RESET)
10624             artwork_element =
10625               (level.use_artwork_element[i] ? level.artwork_element[i] :
10626                stored_player[i].element_nr);
10627
10628 #if USE_GFX_RESET_PLAYER_ARTWORK
10629           if (stored_player[i].artwork_element != artwork_element)
10630             stored_player[i].Frame = 0;
10631 #endif
10632
10633           stored_player[i].artwork_element = artwork_element;
10634
10635           SetPlayerWaiting(&stored_player[i], FALSE);
10636
10637           /* set number of special actions for bored and sleeping animation */
10638           stored_player[i].num_special_action_bored =
10639             get_num_special_action(artwork_element,
10640                                    ACTION_BORING_1, ACTION_BORING_LAST);
10641           stored_player[i].num_special_action_sleeping =
10642             get_num_special_action(artwork_element,
10643                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10644         }
10645       }
10646
10647       break;
10648     }
10649
10650     case CA_SET_PLAYER_INVENTORY:
10651     {
10652       for (i = 0; i < MAX_PLAYERS; i++)
10653       {
10654         struct PlayerInfo *player = &stored_player[i];
10655         int j, k;
10656
10657         if (trigger_player_bits & (1 << i))
10658         {
10659           int inventory_element = action_arg_element;
10660
10661           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10662               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10663               action_arg == CA_ARG_ELEMENT_ACTION)
10664           {
10665             int element = inventory_element;
10666             int collect_count = element_info[element].collect_count_initial;
10667
10668             if (!IS_CUSTOM_ELEMENT(element))
10669               collect_count = 1;
10670
10671             if (collect_count == 0)
10672               player->inventory_infinite_element = element;
10673             else
10674               for (k = 0; k < collect_count; k++)
10675                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10676                   player->inventory_element[player->inventory_size++] =
10677                     element;
10678           }
10679           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10680                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10681                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10682           {
10683             if (player->inventory_infinite_element != EL_UNDEFINED &&
10684                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10685                                      action_arg_element_raw))
10686               player->inventory_infinite_element = EL_UNDEFINED;
10687
10688             for (k = 0, j = 0; j < player->inventory_size; j++)
10689             {
10690               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10691                                         action_arg_element_raw))
10692                 player->inventory_element[k++] = player->inventory_element[j];
10693             }
10694
10695             player->inventory_size = k;
10696           }
10697           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10698           {
10699             if (player->inventory_size > 0)
10700             {
10701               for (j = 0; j < player->inventory_size - 1; j++)
10702                 player->inventory_element[j] = player->inventory_element[j + 1];
10703
10704               player->inventory_size--;
10705             }
10706           }
10707           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10708           {
10709             if (player->inventory_size > 0)
10710               player->inventory_size--;
10711           }
10712           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10713           {
10714             player->inventory_infinite_element = EL_UNDEFINED;
10715             player->inventory_size = 0;
10716           }
10717           else if (action_arg == CA_ARG_INVENTORY_RESET)
10718           {
10719             player->inventory_infinite_element = EL_UNDEFINED;
10720             player->inventory_size = 0;
10721
10722             if (level.use_initial_inventory[i])
10723             {
10724               for (j = 0; j < level.initial_inventory_size[i]; j++)
10725               {
10726                 int element = level.initial_inventory_content[i][j];
10727                 int collect_count = element_info[element].collect_count_initial;
10728
10729                 if (!IS_CUSTOM_ELEMENT(element))
10730                   collect_count = 1;
10731
10732                 if (collect_count == 0)
10733                   player->inventory_infinite_element = element;
10734                 else
10735                   for (k = 0; k < collect_count; k++)
10736                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10737                       player->inventory_element[player->inventory_size++] =
10738                         element;
10739               }
10740             }
10741           }
10742         }
10743       }
10744
10745       break;
10746     }
10747
10748     /* ---------- CE actions  ---------------------------------------------- */
10749
10750     case CA_SET_CE_VALUE:
10751     {
10752 #if USE_NEW_CUSTOM_VALUE
10753       int last_ce_value = CustomValue[x][y];
10754
10755       CustomValue[x][y] = action_arg_number_new;
10756
10757       if (CustomValue[x][y] != last_ce_value)
10758       {
10759         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10760         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10761
10762         if (CustomValue[x][y] == 0)
10763         {
10764           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10765           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10766         }
10767       }
10768 #endif
10769
10770       break;
10771     }
10772
10773     case CA_SET_CE_SCORE:
10774     {
10775 #if USE_NEW_CUSTOM_VALUE
10776       int last_ce_score = ei->collect_score;
10777
10778       ei->collect_score = action_arg_number_new;
10779
10780       if (ei->collect_score != last_ce_score)
10781       {
10782         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10783         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10784
10785         if (ei->collect_score == 0)
10786         {
10787           int xx, yy;
10788
10789           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10790           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10791
10792           /*
10793             This is a very special case that seems to be a mixture between
10794             CheckElementChange() and CheckTriggeredElementChange(): while
10795             the first one only affects single elements that are triggered
10796             directly, the second one affects multiple elements in the playfield
10797             that are triggered indirectly by another element. This is a third
10798             case: Changing the CE score always affects multiple identical CEs,
10799             so every affected CE must be checked, not only the single CE for
10800             which the CE score was changed in the first place (as every instance
10801             of that CE shares the same CE score, and therefore also can change)!
10802           */
10803           SCAN_PLAYFIELD(xx, yy)
10804           {
10805             if (Feld[xx][yy] == element)
10806               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10807                                  CE_SCORE_GETS_ZERO);
10808           }
10809         }
10810       }
10811 #endif
10812
10813       break;
10814     }
10815
10816     case CA_SET_CE_ARTWORK:
10817     {
10818       int artwork_element = action_arg_element;
10819       boolean reset_frame = FALSE;
10820       int xx, yy;
10821
10822       if (action_arg == CA_ARG_ELEMENT_RESET)
10823         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10824                            element);
10825
10826       if (ei->gfx_element != artwork_element)
10827         reset_frame = TRUE;
10828
10829       ei->gfx_element = artwork_element;
10830
10831       SCAN_PLAYFIELD(xx, yy)
10832       {
10833         if (Feld[xx][yy] == element)
10834         {
10835           if (reset_frame)
10836           {
10837             ResetGfxAnimation(xx, yy);
10838             ResetRandomAnimationValue(xx, yy);
10839           }
10840
10841           TEST_DrawLevelField(xx, yy);
10842         }
10843       }
10844
10845       break;
10846     }
10847
10848     /* ---------- engine actions  ------------------------------------------ */
10849
10850     case CA_SET_ENGINE_SCAN_MODE:
10851     {
10852       InitPlayfieldScanMode(action_arg);
10853
10854       break;
10855     }
10856
10857     default:
10858       break;
10859   }
10860 }
10861
10862 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10863 {
10864   int old_element = Feld[x][y];
10865   int new_element = GetElementFromGroupElement(element);
10866   int previous_move_direction = MovDir[x][y];
10867 #if USE_NEW_CUSTOM_VALUE
10868   int last_ce_value = CustomValue[x][y];
10869 #endif
10870   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10871   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10872   boolean add_player_onto_element = (new_element_is_player &&
10873 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10874                                      /* this breaks SnakeBite when a snake is
10875                                         halfway through a door that closes */
10876                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10877                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10878 #endif
10879                                      IS_WALKABLE(old_element));
10880
10881 #if 0
10882   /* check if element under the player changes from accessible to unaccessible
10883      (needed for special case of dropping element which then changes) */
10884   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10885       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10886   {
10887     Bang(x, y);
10888
10889     return;
10890   }
10891 #endif
10892
10893   if (!add_player_onto_element)
10894   {
10895     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10896       RemoveMovingField(x, y);
10897     else
10898       RemoveField(x, y);
10899
10900     Feld[x][y] = new_element;
10901
10902 #if !USE_GFX_RESET_GFX_ANIMATION
10903     ResetGfxAnimation(x, y);
10904     ResetRandomAnimationValue(x, y);
10905 #endif
10906
10907     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10908       MovDir[x][y] = previous_move_direction;
10909
10910 #if USE_NEW_CUSTOM_VALUE
10911     if (element_info[new_element].use_last_ce_value)
10912       CustomValue[x][y] = last_ce_value;
10913 #endif
10914
10915     InitField_WithBug1(x, y, FALSE);
10916
10917     new_element = Feld[x][y];   /* element may have changed */
10918
10919 #if USE_GFX_RESET_GFX_ANIMATION
10920     ResetGfxAnimation(x, y);
10921     ResetRandomAnimationValue(x, y);
10922 #endif
10923
10924     TEST_DrawLevelField(x, y);
10925
10926     if (GFX_CRUMBLED(new_element))
10927       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10928   }
10929
10930 #if 1
10931   /* check if element under the player changes from accessible to unaccessible
10932      (needed for special case of dropping element which then changes) */
10933   /* (must be checked after creating new element for walkable group elements) */
10934 #if USE_FIX_KILLED_BY_NON_WALKABLE
10935   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10936       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10937   {
10938     Bang(x, y);
10939
10940     return;
10941   }
10942 #else
10943   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10944       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10945   {
10946     Bang(x, y);
10947
10948     return;
10949   }
10950 #endif
10951 #endif
10952
10953   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10954   if (new_element_is_player)
10955     RelocatePlayer(x, y, new_element);
10956
10957   if (is_change)
10958     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10959
10960   TestIfBadThingTouchesPlayer(x, y);
10961   TestIfPlayerTouchesCustomElement(x, y);
10962   TestIfElementTouchesCustomElement(x, y);
10963 }
10964
10965 static void CreateField(int x, int y, int element)
10966 {
10967   CreateFieldExt(x, y, element, FALSE);
10968 }
10969
10970 static void CreateElementFromChange(int x, int y, int element)
10971 {
10972   element = GET_VALID_RUNTIME_ELEMENT(element);
10973
10974 #if USE_STOP_CHANGED_ELEMENTS
10975   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10976   {
10977     int old_element = Feld[x][y];
10978
10979     /* prevent changed element from moving in same engine frame
10980        unless both old and new element can either fall or move */
10981     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10982         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10983       Stop[x][y] = TRUE;
10984   }
10985 #endif
10986
10987   CreateFieldExt(x, y, element, TRUE);
10988 }
10989
10990 static boolean ChangeElement(int x, int y, int element, int page)
10991 {
10992   struct ElementInfo *ei = &element_info[element];
10993   struct ElementChangeInfo *change = &ei->change_page[page];
10994   int ce_value = CustomValue[x][y];
10995   int ce_score = ei->collect_score;
10996   int target_element;
10997   int old_element = Feld[x][y];
10998
10999   /* always use default change event to prevent running into a loop */
11000   if (ChangeEvent[x][y] == -1)
11001     ChangeEvent[x][y] = CE_DELAY;
11002
11003   if (ChangeEvent[x][y] == CE_DELAY)
11004   {
11005     /* reset actual trigger element, trigger player and action element */
11006     change->actual_trigger_element = EL_EMPTY;
11007     change->actual_trigger_player = EL_EMPTY;
11008     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11009     change->actual_trigger_side = CH_SIDE_NONE;
11010     change->actual_trigger_ce_value = 0;
11011     change->actual_trigger_ce_score = 0;
11012   }
11013
11014   /* do not change elements more than a specified maximum number of changes */
11015   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11016     return FALSE;
11017
11018   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11019
11020   if (change->explode)
11021   {
11022     Bang(x, y);
11023
11024     return TRUE;
11025   }
11026
11027   if (change->use_target_content)
11028   {
11029     boolean complete_replace = TRUE;
11030     boolean can_replace[3][3];
11031     int xx, yy;
11032
11033     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11034     {
11035       boolean is_empty;
11036       boolean is_walkable;
11037       boolean is_diggable;
11038       boolean is_collectible;
11039       boolean is_removable;
11040       boolean is_destructible;
11041       int ex = x + xx - 1;
11042       int ey = y + yy - 1;
11043       int content_element = change->target_content.e[xx][yy];
11044       int e;
11045
11046       can_replace[xx][yy] = TRUE;
11047
11048       if (ex == x && ey == y)   /* do not check changing element itself */
11049         continue;
11050
11051       if (content_element == EL_EMPTY_SPACE)
11052       {
11053         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11054
11055         continue;
11056       }
11057
11058       if (!IN_LEV_FIELD(ex, ey))
11059       {
11060         can_replace[xx][yy] = FALSE;
11061         complete_replace = FALSE;
11062
11063         continue;
11064       }
11065
11066       e = Feld[ex][ey];
11067
11068       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11069         e = MovingOrBlocked2Element(ex, ey);
11070
11071       is_empty = (IS_FREE(ex, ey) ||
11072                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11073
11074       is_walkable     = (is_empty || IS_WALKABLE(e));
11075       is_diggable     = (is_empty || IS_DIGGABLE(e));
11076       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11077       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11078       is_removable    = (is_diggable || is_collectible);
11079
11080       can_replace[xx][yy] =
11081         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11082           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11083           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11084           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11085           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11086           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11087          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11088
11089       if (!can_replace[xx][yy])
11090         complete_replace = FALSE;
11091     }
11092
11093     if (!change->only_if_complete || complete_replace)
11094     {
11095       boolean something_has_changed = FALSE;
11096
11097       if (change->only_if_complete && change->use_random_replace &&
11098           RND(100) < change->random_percentage)
11099         return FALSE;
11100
11101       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11102       {
11103         int ex = x + xx - 1;
11104         int ey = y + yy - 1;
11105         int content_element;
11106
11107         if (can_replace[xx][yy] && (!change->use_random_replace ||
11108                                     RND(100) < change->random_percentage))
11109         {
11110           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11111             RemoveMovingField(ex, ey);
11112
11113           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11114
11115           content_element = change->target_content.e[xx][yy];
11116           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11117                                               ce_value, ce_score);
11118
11119           CreateElementFromChange(ex, ey, target_element);
11120
11121           something_has_changed = TRUE;
11122
11123           /* for symmetry reasons, freeze newly created border elements */
11124           if (ex != x || ey != y)
11125             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11126         }
11127       }
11128
11129       if (something_has_changed)
11130       {
11131         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11132         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11133       }
11134     }
11135   }
11136   else
11137   {
11138     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11139                                         ce_value, ce_score);
11140
11141     if (element == EL_DIAGONAL_GROWING ||
11142         element == EL_DIAGONAL_SHRINKING)
11143     {
11144       target_element = Store[x][y];
11145
11146       Store[x][y] = EL_EMPTY;
11147     }
11148
11149     CreateElementFromChange(x, y, target_element);
11150
11151     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11152     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11153   }
11154
11155   /* this uses direct change before indirect change */
11156   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11157
11158   return TRUE;
11159 }
11160
11161 #if USE_NEW_DELAYED_ACTION
11162
11163 static void HandleElementChange(int x, int y, int page)
11164 {
11165   int element = MovingOrBlocked2Element(x, y);
11166   struct ElementInfo *ei = &element_info[element];
11167   struct ElementChangeInfo *change = &ei->change_page[page];
11168   boolean handle_action_before_change = FALSE;
11169
11170 #ifdef DEBUG
11171   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11172       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11173   {
11174     printf("\n\n");
11175     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11176            x, y, element, element_info[element].token_name);
11177     printf("HandleElementChange(): This should never happen!\n");
11178     printf("\n\n");
11179   }
11180 #endif
11181
11182   /* this can happen with classic bombs on walkable, changing elements */
11183   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11184   {
11185 #if 0
11186     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11187       ChangeDelay[x][y] = 0;
11188 #endif
11189
11190     return;
11191   }
11192
11193   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11194   {
11195     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11196
11197     if (change->can_change)
11198     {
11199 #if 1
11200       /* !!! not clear why graphic animation should be reset at all here !!! */
11201       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11202 #if USE_GFX_RESET_WHEN_NOT_MOVING
11203       /* when a custom element is about to change (for example by change delay),
11204          do not reset graphic animation when the custom element is moving */
11205       if (!IS_MOVING(x, y))
11206 #endif
11207       {
11208         ResetGfxAnimation(x, y);
11209         ResetRandomAnimationValue(x, y);
11210       }
11211 #endif
11212
11213       if (change->pre_change_function)
11214         change->pre_change_function(x, y);
11215     }
11216   }
11217
11218   ChangeDelay[x][y]--;
11219
11220   if (ChangeDelay[x][y] != 0)           /* continue element change */
11221   {
11222     if (change->can_change)
11223     {
11224       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11225
11226       if (IS_ANIMATED(graphic))
11227         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11228
11229       if (change->change_function)
11230         change->change_function(x, y);
11231     }
11232   }
11233   else                                  /* finish element change */
11234   {
11235     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11236     {
11237       page = ChangePage[x][y];
11238       ChangePage[x][y] = -1;
11239
11240       change = &ei->change_page[page];
11241     }
11242
11243     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11244     {
11245       ChangeDelay[x][y] = 1;            /* try change after next move step */
11246       ChangePage[x][y] = page;          /* remember page to use for change */
11247
11248       return;
11249     }
11250
11251 #if 1
11252     /* special case: set new level random seed before changing element */
11253     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11254       handle_action_before_change = TRUE;
11255
11256     if (change->has_action && handle_action_before_change)
11257       ExecuteCustomElementAction(x, y, element, page);
11258 #endif
11259
11260     if (change->can_change)
11261     {
11262       if (ChangeElement(x, y, element, page))
11263       {
11264         if (change->post_change_function)
11265           change->post_change_function(x, y);
11266       }
11267     }
11268
11269     if (change->has_action && !handle_action_before_change)
11270       ExecuteCustomElementAction(x, y, element, page);
11271   }
11272 }
11273
11274 #else
11275
11276 static void HandleElementChange(int x, int y, int page)
11277 {
11278   int element = MovingOrBlocked2Element(x, y);
11279   struct ElementInfo *ei = &element_info[element];
11280   struct ElementChangeInfo *change = &ei->change_page[page];
11281
11282 #ifdef DEBUG
11283   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11284   {
11285     printf("\n\n");
11286     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11287            x, y, element, element_info[element].token_name);
11288     printf("HandleElementChange(): This should never happen!\n");
11289     printf("\n\n");
11290   }
11291 #endif
11292
11293   /* this can happen with classic bombs on walkable, changing elements */
11294   if (!CAN_CHANGE(element))
11295   {
11296 #if 0
11297     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11298       ChangeDelay[x][y] = 0;
11299 #endif
11300
11301     return;
11302   }
11303
11304   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11305   {
11306     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11307
11308     ResetGfxAnimation(x, y);
11309     ResetRandomAnimationValue(x, y);
11310
11311     if (change->pre_change_function)
11312       change->pre_change_function(x, y);
11313   }
11314
11315   ChangeDelay[x][y]--;
11316
11317   if (ChangeDelay[x][y] != 0)           /* continue element change */
11318   {
11319     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11320
11321     if (IS_ANIMATED(graphic))
11322       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11323
11324     if (change->change_function)
11325       change->change_function(x, y);
11326   }
11327   else                                  /* finish element change */
11328   {
11329     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11330     {
11331       page = ChangePage[x][y];
11332       ChangePage[x][y] = -1;
11333
11334       change = &ei->change_page[page];
11335     }
11336
11337     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11338     {
11339       ChangeDelay[x][y] = 1;            /* try change after next move step */
11340       ChangePage[x][y] = page;          /* remember page to use for change */
11341
11342       return;
11343     }
11344
11345     if (ChangeElement(x, y, element, page))
11346     {
11347       if (change->post_change_function)
11348         change->post_change_function(x, y);
11349     }
11350   }
11351 }
11352
11353 #endif
11354
11355 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11356                                               int trigger_element,
11357                                               int trigger_event,
11358                                               int trigger_player,
11359                                               int trigger_side,
11360                                               int trigger_page)
11361 {
11362   boolean change_done_any = FALSE;
11363   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11364   int i;
11365
11366   if (!(trigger_events[trigger_element][trigger_event]))
11367     return FALSE;
11368
11369 #if 0
11370   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11371          trigger_event, recursion_loop_depth, recursion_loop_detected,
11372          recursion_loop_element, EL_NAME(recursion_loop_element));
11373 #endif
11374
11375   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11376
11377   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11378   {
11379     int element = EL_CUSTOM_START + i;
11380     boolean change_done = FALSE;
11381     int p;
11382
11383     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11384         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11385       continue;
11386
11387     for (p = 0; p < element_info[element].num_change_pages; p++)
11388     {
11389       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11390
11391       if (change->can_change_or_has_action &&
11392           change->has_event[trigger_event] &&
11393           change->trigger_side & trigger_side &&
11394           change->trigger_player & trigger_player &&
11395           change->trigger_page & trigger_page_bits &&
11396           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11397       {
11398         change->actual_trigger_element = trigger_element;
11399         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11400         change->actual_trigger_player_bits = trigger_player;
11401         change->actual_trigger_side = trigger_side;
11402         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11403         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11404
11405 #if 0
11406         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11407                element, EL_NAME(element), p);
11408 #endif
11409
11410         if ((change->can_change && !change_done) || change->has_action)
11411         {
11412           int x, y;
11413
11414           SCAN_PLAYFIELD(x, y)
11415           {
11416             if (Feld[x][y] == element)
11417             {
11418               if (change->can_change && !change_done)
11419               {
11420 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11421                 /* if element already changed in this frame, not only prevent
11422                    another element change (checked in ChangeElement()), but
11423                    also prevent additional element actions for this element */
11424
11425                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11426                     !level.use_action_after_change_bug)
11427                   continue;
11428 #endif
11429
11430 #if 0
11431                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11432                        element, EL_NAME(element), p);
11433 #endif
11434
11435                 ChangeDelay[x][y] = 1;
11436                 ChangeEvent[x][y] = trigger_event;
11437
11438                 HandleElementChange(x, y, p);
11439               }
11440 #if USE_NEW_DELAYED_ACTION
11441               else if (change->has_action)
11442               {
11443 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11444                 /* if element already changed in this frame, not only prevent
11445                    another element change (checked in ChangeElement()), but
11446                    also prevent additional element actions for this element */
11447
11448                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11449                     !level.use_action_after_change_bug)
11450                   continue;
11451 #endif
11452
11453
11454 #if 0
11455                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11456                        element, EL_NAME(element), p);
11457 #endif
11458
11459                 ExecuteCustomElementAction(x, y, element, p);
11460                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11461               }
11462 #else
11463               if (change->has_action)
11464               {
11465                 ExecuteCustomElementAction(x, y, element, p);
11466                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11467               }
11468 #endif
11469             }
11470           }
11471
11472           if (change->can_change)
11473           {
11474             change_done = TRUE;
11475             change_done_any = TRUE;
11476
11477 #if 0
11478             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11479                    element, EL_NAME(element), p);
11480 #endif
11481
11482           }
11483         }
11484       }
11485     }
11486   }
11487
11488   RECURSION_LOOP_DETECTION_END();
11489
11490   return change_done_any;
11491 }
11492
11493 static boolean CheckElementChangeExt(int x, int y,
11494                                      int element,
11495                                      int trigger_element,
11496                                      int trigger_event,
11497                                      int trigger_player,
11498                                      int trigger_side)
11499 {
11500   boolean change_done = FALSE;
11501   int p;
11502
11503   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11504       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11505     return FALSE;
11506
11507   if (Feld[x][y] == EL_BLOCKED)
11508   {
11509     Blocked2Moving(x, y, &x, &y);
11510     element = Feld[x][y];
11511   }
11512
11513 #if 0
11514   /* check if element has already changed */
11515   if (Feld[x][y] != element)
11516     return FALSE;
11517 #else
11518   /* check if element has already changed or is about to change after moving */
11519   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11520        Feld[x][y] != element) ||
11521
11522       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11523        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11524         ChangePage[x][y] != -1)))
11525     return FALSE;
11526 #endif
11527
11528 #if 0
11529   printf("::: CheckElementChangeExt %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 #if 0
11537   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11538 #endif
11539
11540   for (p = 0; p < element_info[element].num_change_pages; p++)
11541   {
11542     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11543
11544     /* check trigger element for all events where the element that is checked
11545        for changing interacts with a directly adjacent element -- this is
11546        different to element changes that affect other elements to change on the
11547        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11548     boolean check_trigger_element =
11549       (trigger_event == CE_TOUCHING_X ||
11550        trigger_event == CE_HITTING_X ||
11551        trigger_event == CE_HIT_BY_X ||
11552 #if 1
11553        /* this one was forgotten until 3.2.3 */
11554        trigger_event == CE_DIGGING_X);
11555 #endif
11556
11557     if (change->can_change_or_has_action &&
11558         change->has_event[trigger_event] &&
11559         change->trigger_side & trigger_side &&
11560         change->trigger_player & trigger_player &&
11561         (!check_trigger_element ||
11562          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11563     {
11564       change->actual_trigger_element = trigger_element;
11565       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11566       change->actual_trigger_player_bits = trigger_player;
11567       change->actual_trigger_side = trigger_side;
11568       change->actual_trigger_ce_value = CustomValue[x][y];
11569       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11570
11571       /* special case: trigger element not at (x,y) position for some events */
11572       if (check_trigger_element)
11573       {
11574         static struct
11575         {
11576           int dx, dy;
11577         } move_xy[] =
11578           {
11579             {  0,  0 },
11580             { -1,  0 },
11581             { +1,  0 },
11582             {  0,  0 },
11583             {  0, -1 },
11584             {  0,  0 }, { 0, 0 }, { 0, 0 },
11585             {  0, +1 }
11586           };
11587
11588         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11589         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11590
11591         change->actual_trigger_ce_value = CustomValue[xx][yy];
11592         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11593       }
11594
11595       if (change->can_change && !change_done)
11596       {
11597         ChangeDelay[x][y] = 1;
11598         ChangeEvent[x][y] = trigger_event;
11599
11600         HandleElementChange(x, y, p);
11601
11602         change_done = TRUE;
11603       }
11604 #if USE_NEW_DELAYED_ACTION
11605       else if (change->has_action)
11606       {
11607         ExecuteCustomElementAction(x, y, element, p);
11608         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11609       }
11610 #else
11611       if (change->has_action)
11612       {
11613         ExecuteCustomElementAction(x, y, element, p);
11614         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11615       }
11616 #endif
11617     }
11618   }
11619
11620   RECURSION_LOOP_DETECTION_END();
11621
11622   return change_done;
11623 }
11624
11625 static void PlayPlayerSound(struct PlayerInfo *player)
11626 {
11627   int jx = player->jx, jy = player->jy;
11628   int sound_element = player->artwork_element;
11629   int last_action = player->last_action_waiting;
11630   int action = player->action_waiting;
11631
11632   if (player->is_waiting)
11633   {
11634     if (action != last_action)
11635       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11636     else
11637       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11638   }
11639   else
11640   {
11641     if (action != last_action)
11642       StopSound(element_info[sound_element].sound[last_action]);
11643
11644     if (last_action == ACTION_SLEEPING)
11645       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11646   }
11647 }
11648
11649 static void PlayAllPlayersSound()
11650 {
11651   int i;
11652
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654     if (stored_player[i].active)
11655       PlayPlayerSound(&stored_player[i]);
11656 }
11657
11658 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11659 {
11660   boolean last_waiting = player->is_waiting;
11661   int move_dir = player->MovDir;
11662
11663   player->dir_waiting = move_dir;
11664   player->last_action_waiting = player->action_waiting;
11665
11666   if (is_waiting)
11667   {
11668     if (!last_waiting)          /* not waiting -> waiting */
11669     {
11670       player->is_waiting = TRUE;
11671
11672       player->frame_counter_bored =
11673         FrameCounter +
11674         game.player_boring_delay_fixed +
11675         GetSimpleRandom(game.player_boring_delay_random);
11676       player->frame_counter_sleeping =
11677         FrameCounter +
11678         game.player_sleeping_delay_fixed +
11679         GetSimpleRandom(game.player_sleeping_delay_random);
11680
11681       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11682     }
11683
11684     if (game.player_sleeping_delay_fixed +
11685         game.player_sleeping_delay_random > 0 &&
11686         player->anim_delay_counter == 0 &&
11687         player->post_delay_counter == 0 &&
11688         FrameCounter >= player->frame_counter_sleeping)
11689       player->is_sleeping = TRUE;
11690     else if (game.player_boring_delay_fixed +
11691              game.player_boring_delay_random > 0 &&
11692              FrameCounter >= player->frame_counter_bored)
11693       player->is_bored = TRUE;
11694
11695     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11696                               player->is_bored ? ACTION_BORING :
11697                               ACTION_WAITING);
11698
11699     if (player->is_sleeping && player->use_murphy)
11700     {
11701       /* special case for sleeping Murphy when leaning against non-free tile */
11702
11703       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11704           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11705            !IS_MOVING(player->jx - 1, player->jy)))
11706         move_dir = MV_LEFT;
11707       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11708                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11709                 !IS_MOVING(player->jx + 1, player->jy)))
11710         move_dir = MV_RIGHT;
11711       else
11712         player->is_sleeping = FALSE;
11713
11714       player->dir_waiting = move_dir;
11715     }
11716
11717     if (player->is_sleeping)
11718     {
11719       if (player->num_special_action_sleeping > 0)
11720       {
11721         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11722         {
11723           int last_special_action = player->special_action_sleeping;
11724           int num_special_action = player->num_special_action_sleeping;
11725           int special_action =
11726             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11727              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11728              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11729              last_special_action + 1 : ACTION_SLEEPING);
11730           int special_graphic =
11731             el_act_dir2img(player->artwork_element, special_action, move_dir);
11732
11733           player->anim_delay_counter =
11734             graphic_info[special_graphic].anim_delay_fixed +
11735             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11736           player->post_delay_counter =
11737             graphic_info[special_graphic].post_delay_fixed +
11738             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11739
11740           player->special_action_sleeping = special_action;
11741         }
11742
11743         if (player->anim_delay_counter > 0)
11744         {
11745           player->action_waiting = player->special_action_sleeping;
11746           player->anim_delay_counter--;
11747         }
11748         else if (player->post_delay_counter > 0)
11749         {
11750           player->post_delay_counter--;
11751         }
11752       }
11753     }
11754     else if (player->is_bored)
11755     {
11756       if (player->num_special_action_bored > 0)
11757       {
11758         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11759         {
11760           int special_action =
11761             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11762           int special_graphic =
11763             el_act_dir2img(player->artwork_element, special_action, move_dir);
11764
11765           player->anim_delay_counter =
11766             graphic_info[special_graphic].anim_delay_fixed +
11767             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11768           player->post_delay_counter =
11769             graphic_info[special_graphic].post_delay_fixed +
11770             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11771
11772           player->special_action_bored = special_action;
11773         }
11774
11775         if (player->anim_delay_counter > 0)
11776         {
11777           player->action_waiting = player->special_action_bored;
11778           player->anim_delay_counter--;
11779         }
11780         else if (player->post_delay_counter > 0)
11781         {
11782           player->post_delay_counter--;
11783         }
11784       }
11785     }
11786   }
11787   else if (last_waiting)        /* waiting -> not waiting */
11788   {
11789     player->is_waiting = FALSE;
11790     player->is_bored = FALSE;
11791     player->is_sleeping = FALSE;
11792
11793     player->frame_counter_bored = -1;
11794     player->frame_counter_sleeping = -1;
11795
11796     player->anim_delay_counter = 0;
11797     player->post_delay_counter = 0;
11798
11799     player->dir_waiting = player->MovDir;
11800     player->action_waiting = ACTION_DEFAULT;
11801
11802     player->special_action_bored = ACTION_DEFAULT;
11803     player->special_action_sleeping = ACTION_DEFAULT;
11804   }
11805 }
11806
11807 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11808 {
11809   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11810   int left      = player_action & JOY_LEFT;
11811   int right     = player_action & JOY_RIGHT;
11812   int up        = player_action & JOY_UP;
11813   int down      = player_action & JOY_DOWN;
11814   int button1   = player_action & JOY_BUTTON_1;
11815   int button2   = player_action & JOY_BUTTON_2;
11816   int dx        = (left ? -1 : right ? 1 : 0);
11817   int dy        = (up   ? -1 : down  ? 1 : 0);
11818
11819   if (!player->active || tape.pausing)
11820     return 0;
11821
11822   if (player_action)
11823   {
11824     if (button1)
11825       snapped = SnapField(player, dx, dy);
11826     else
11827     {
11828       if (button2)
11829         dropped = DropElement(player);
11830
11831       moved = MovePlayer(player, dx, dy);
11832     }
11833
11834     if (tape.single_step && tape.recording && !tape.pausing)
11835     {
11836       if (button1 || (dropped && !moved))
11837       {
11838         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11839         SnapField(player, 0, 0);                /* stop snapping */
11840       }
11841     }
11842
11843     SetPlayerWaiting(player, FALSE);
11844
11845     return player_action;
11846   }
11847   else
11848   {
11849     /* no actions for this player (no input at player's configured device) */
11850
11851     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11852     SnapField(player, 0, 0);
11853     CheckGravityMovementWhenNotMoving(player);
11854
11855     if (player->MovPos == 0)
11856       SetPlayerWaiting(player, TRUE);
11857
11858     if (player->MovPos == 0)    /* needed for tape.playing */
11859       player->is_moving = FALSE;
11860
11861     player->is_dropping = FALSE;
11862     player->is_dropping_pressed = FALSE;
11863     player->drop_pressed_delay = 0;
11864
11865     return 0;
11866   }
11867 }
11868
11869 static void CheckLevelTime()
11870 {
11871   int i;
11872
11873   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11874   {
11875     if (level.native_em_level->lev->home == 0)  /* all players at home */
11876     {
11877       PlayerWins(local_player);
11878
11879       AllPlayersGone = TRUE;
11880
11881       level.native_em_level->lev->home = -1;
11882     }
11883
11884     if (level.native_em_level->ply[0]->alive == 0 &&
11885         level.native_em_level->ply[1]->alive == 0 &&
11886         level.native_em_level->ply[2]->alive == 0 &&
11887         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11888       AllPlayersGone = TRUE;
11889   }
11890
11891   if (TimeFrames >= FRAMES_PER_SECOND)
11892   {
11893     TimeFrames = 0;
11894     TapeTime++;
11895
11896     for (i = 0; i < MAX_PLAYERS; i++)
11897     {
11898       struct PlayerInfo *player = &stored_player[i];
11899
11900       if (SHIELD_ON(player))
11901       {
11902         player->shield_normal_time_left--;
11903
11904         if (player->shield_deadly_time_left > 0)
11905           player->shield_deadly_time_left--;
11906       }
11907     }
11908
11909     if (!local_player->LevelSolved && !level.use_step_counter)
11910     {
11911       TimePlayed++;
11912
11913       if (TimeLeft > 0)
11914       {
11915         TimeLeft--;
11916
11917         if (TimeLeft <= 10 && setup.time_limit)
11918           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11919
11920 #if 1
11921         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11922
11923         DisplayGameControlValues();
11924 #else
11925         DrawGameValue_Time(TimeLeft);
11926 #endif
11927
11928         if (!TimeLeft && setup.time_limit)
11929         {
11930           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11931             level.native_em_level->lev->killed_out_of_time = TRUE;
11932           else
11933             for (i = 0; i < MAX_PLAYERS; i++)
11934               KillPlayer(&stored_player[i]);
11935         }
11936       }
11937 #if 1
11938       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11939       {
11940         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11941
11942         DisplayGameControlValues();
11943       }
11944 #else
11945       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11946         DrawGameValue_Time(TimePlayed);
11947 #endif
11948
11949       level.native_em_level->lev->time =
11950         (level.time == 0 ? TimePlayed : TimeLeft);
11951     }
11952
11953     if (tape.recording || tape.playing)
11954       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11955   }
11956
11957 #if 1
11958   UpdateAndDisplayGameControlValues();
11959 #else
11960   UpdateGameDoorValues();
11961   DrawGameDoorValues();
11962 #endif
11963 }
11964
11965 void AdvanceFrameAndPlayerCounters(int player_nr)
11966 {
11967   int i;
11968
11969   /* advance frame counters (global frame counter and time frame counter) */
11970   FrameCounter++;
11971   TimeFrames++;
11972
11973   /* advance player counters (counters for move delay, move animation etc.) */
11974   for (i = 0; i < MAX_PLAYERS; i++)
11975   {
11976     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11977     int move_delay_value = stored_player[i].move_delay_value;
11978     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11979
11980     if (!advance_player_counters)       /* not all players may be affected */
11981       continue;
11982
11983 #if USE_NEW_PLAYER_ANIM
11984     if (move_frames == 0)       /* less than one move per game frame */
11985     {
11986       int stepsize = TILEX / move_delay_value;
11987       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11988       int count = (stored_player[i].is_moving ?
11989                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11990
11991       if (count % delay == 0)
11992         move_frames = 1;
11993     }
11994 #endif
11995
11996     stored_player[i].Frame += move_frames;
11997
11998     if (stored_player[i].MovPos != 0)
11999       stored_player[i].StepFrame += move_frames;
12000
12001     if (stored_player[i].move_delay > 0)
12002       stored_player[i].move_delay--;
12003
12004     /* due to bugs in previous versions, counter must count up, not down */
12005     if (stored_player[i].push_delay != -1)
12006       stored_player[i].push_delay++;
12007
12008     if (stored_player[i].drop_delay > 0)
12009       stored_player[i].drop_delay--;
12010
12011     if (stored_player[i].is_dropping_pressed)
12012       stored_player[i].drop_pressed_delay++;
12013   }
12014 }
12015
12016 void StartGameActions(boolean init_network_game, boolean record_tape,
12017                       long random_seed)
12018 {
12019   unsigned long new_random_seed = InitRND(random_seed);
12020
12021   if (record_tape)
12022     TapeStartRecording(new_random_seed);
12023
12024 #if defined(NETWORK_AVALIABLE)
12025   if (init_network_game)
12026   {
12027     SendToServer_StartPlaying();
12028
12029     return;
12030   }
12031 #endif
12032
12033   InitGame();
12034 }
12035
12036 void GameActions()
12037 {
12038   static unsigned long game_frame_delay = 0;
12039   unsigned long game_frame_delay_value;
12040   byte *recorded_player_action;
12041   byte summarized_player_action = 0;
12042   byte tape_action[MAX_PLAYERS];
12043   int i;
12044
12045   /* detect endless loops, caused by custom element programming */
12046   if (recursion_loop_detected && recursion_loop_depth == 0)
12047   {
12048     char *message = getStringCat3("Internal Error ! Element ",
12049                                   EL_NAME(recursion_loop_element),
12050                                   " caused endless loop ! Quit the game ?");
12051
12052     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12053           EL_NAME(recursion_loop_element));
12054
12055     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12056
12057     recursion_loop_detected = FALSE;    /* if game should be continued */
12058
12059     free(message);
12060
12061     return;
12062   }
12063
12064   if (game.restart_level)
12065     StartGameActions(options.network, setup.autorecord, level.random_seed);
12066
12067   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12068   {
12069     if (level.native_em_level->lev->home == 0)  /* all players at home */
12070     {
12071       PlayerWins(local_player);
12072
12073       AllPlayersGone = TRUE;
12074
12075       level.native_em_level->lev->home = -1;
12076     }
12077
12078     if (level.native_em_level->ply[0]->alive == 0 &&
12079         level.native_em_level->ply[1]->alive == 0 &&
12080         level.native_em_level->ply[2]->alive == 0 &&
12081         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12082       AllPlayersGone = TRUE;
12083   }
12084
12085   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12086     GameWon();
12087
12088   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12089     TapeStop();
12090
12091   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12092     return;
12093
12094   game_frame_delay_value =
12095     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12096
12097   if (tape.playing && tape.warp_forward && !tape.pausing)
12098     game_frame_delay_value = 0;
12099
12100   /* ---------- main game synchronization point ---------- */
12101
12102   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12103
12104   if (network_playing && !network_player_action_received)
12105   {
12106     /* try to get network player actions in time */
12107
12108 #if defined(NETWORK_AVALIABLE)
12109     /* last chance to get network player actions without main loop delay */
12110     HandleNetworking();
12111 #endif
12112
12113     /* game was quit by network peer */
12114     if (game_status != GAME_MODE_PLAYING)
12115       return;
12116
12117     if (!network_player_action_received)
12118       return;           /* failed to get network player actions in time */
12119
12120     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12121   }
12122
12123   if (tape.pausing)
12124     return;
12125
12126   /* at this point we know that we really continue executing the game */
12127
12128   network_player_action_received = FALSE;
12129
12130   /* when playing tape, read previously recorded player input from tape data */
12131   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12132
12133 #if 1
12134   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12135   if (tape.pausing)
12136     return;
12137 #endif
12138
12139   if (tape.set_centered_player)
12140   {
12141     game.centered_player_nr_next = tape.centered_player_nr_next;
12142     game.set_centered_player = TRUE;
12143   }
12144
12145   for (i = 0; i < MAX_PLAYERS; i++)
12146   {
12147     summarized_player_action |= stored_player[i].action;
12148
12149     if (!network_playing)
12150       stored_player[i].effective_action = stored_player[i].action;
12151   }
12152
12153 #if defined(NETWORK_AVALIABLE)
12154   if (network_playing)
12155     SendToServer_MovePlayer(summarized_player_action);
12156 #endif
12157
12158   if (!options.network && !setup.team_mode)
12159     local_player->effective_action = summarized_player_action;
12160
12161   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12162   {
12163     for (i = 0; i < MAX_PLAYERS; i++)
12164       stored_player[i].effective_action =
12165         (i == game.centered_player_nr ? summarized_player_action : 0);
12166   }
12167
12168   if (recorded_player_action != NULL)
12169     for (i = 0; i < MAX_PLAYERS; i++)
12170       stored_player[i].effective_action = recorded_player_action[i];
12171
12172   for (i = 0; i < MAX_PLAYERS; i++)
12173   {
12174     tape_action[i] = stored_player[i].effective_action;
12175
12176     /* (this can only happen in the R'n'D game engine) */
12177     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12178       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12179   }
12180
12181   /* only record actions from input devices, but not programmed actions */
12182   if (tape.recording)
12183     TapeRecordAction(tape_action);
12184
12185   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12186   {
12187     GameActions_EM_Main();
12188   }
12189   else
12190   {
12191     GameActions_RND();
12192   }
12193 }
12194
12195 void GameActions_EM_Main()
12196 {
12197   byte effective_action[MAX_PLAYERS];
12198   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12199   int i;
12200
12201   for (i = 0; i < MAX_PLAYERS; i++)
12202     effective_action[i] = stored_player[i].effective_action;
12203
12204   GameActions_EM(effective_action, warp_mode);
12205
12206   CheckLevelTime();
12207
12208   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12209 }
12210
12211 void GameActions_RND()
12212 {
12213   int magic_wall_x = 0, magic_wall_y = 0;
12214   int i, x, y, element, graphic;
12215
12216   InitPlayfieldScanModeVars();
12217
12218 #if USE_ONE_MORE_CHANGE_PER_FRAME
12219   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12220   {
12221     SCAN_PLAYFIELD(x, y)
12222     {
12223       ChangeCount[x][y] = 0;
12224       ChangeEvent[x][y] = -1;
12225     }
12226   }
12227 #endif
12228
12229   if (game.set_centered_player)
12230   {
12231     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12232
12233     /* switching to "all players" only possible if all players fit to screen */
12234     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12235     {
12236       game.centered_player_nr_next = game.centered_player_nr;
12237       game.set_centered_player = FALSE;
12238     }
12239
12240     /* do not switch focus to non-existing (or non-active) player */
12241     if (game.centered_player_nr_next >= 0 &&
12242         !stored_player[game.centered_player_nr_next].active)
12243     {
12244       game.centered_player_nr_next = game.centered_player_nr;
12245       game.set_centered_player = FALSE;
12246     }
12247   }
12248
12249   if (game.set_centered_player &&
12250       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12251   {
12252     int sx, sy;
12253
12254     if (game.centered_player_nr_next == -1)
12255     {
12256       setScreenCenteredToAllPlayers(&sx, &sy);
12257     }
12258     else
12259     {
12260       sx = stored_player[game.centered_player_nr_next].jx;
12261       sy = stored_player[game.centered_player_nr_next].jy;
12262     }
12263
12264     game.centered_player_nr = game.centered_player_nr_next;
12265     game.set_centered_player = FALSE;
12266
12267     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12268     DrawGameDoorValues();
12269   }
12270
12271   for (i = 0; i < MAX_PLAYERS; i++)
12272   {
12273     int actual_player_action = stored_player[i].effective_action;
12274
12275 #if 1
12276     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12277        - rnd_equinox_tetrachloride 048
12278        - rnd_equinox_tetrachloride_ii 096
12279        - rnd_emanuel_schmieg 002
12280        - doctor_sloan_ww 001, 020
12281     */
12282     if (stored_player[i].MovPos == 0)
12283       CheckGravityMovement(&stored_player[i]);
12284 #endif
12285
12286     /* overwrite programmed action with tape action */
12287     if (stored_player[i].programmed_action)
12288       actual_player_action = stored_player[i].programmed_action;
12289
12290     PlayerActions(&stored_player[i], actual_player_action);
12291
12292     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12293   }
12294
12295   ScrollScreen(NULL, SCROLL_GO_ON);
12296
12297   /* for backwards compatibility, the following code emulates a fixed bug that
12298      occured when pushing elements (causing elements that just made their last
12299      pushing step to already (if possible) make their first falling step in the
12300      same game frame, which is bad); this code is also needed to use the famous
12301      "spring push bug" which is used in older levels and might be wanted to be
12302      used also in newer levels, but in this case the buggy pushing code is only
12303      affecting the "spring" element and no other elements */
12304
12305   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12306   {
12307     for (i = 0; i < MAX_PLAYERS; i++)
12308     {
12309       struct PlayerInfo *player = &stored_player[i];
12310       int x = player->jx;
12311       int y = player->jy;
12312
12313       if (player->active && player->is_pushing && player->is_moving &&
12314           IS_MOVING(x, y) &&
12315           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12316            Feld[x][y] == EL_SPRING))
12317       {
12318         ContinueMoving(x, y);
12319
12320         /* continue moving after pushing (this is actually a bug) */
12321         if (!IS_MOVING(x, y))
12322           Stop[x][y] = FALSE;
12323       }
12324     }
12325   }
12326
12327 #if 0
12328   debug_print_timestamp(0, "start main loop profiling");
12329 #endif
12330
12331   SCAN_PLAYFIELD(x, y)
12332   {
12333     ChangeCount[x][y] = 0;
12334     ChangeEvent[x][y] = -1;
12335
12336     /* this must be handled before main playfield loop */
12337     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12338     {
12339       MovDelay[x][y]--;
12340       if (MovDelay[x][y] <= 0)
12341         RemoveField(x, y);
12342     }
12343
12344 #if USE_NEW_SNAP_DELAY
12345     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12346     {
12347       MovDelay[x][y]--;
12348       if (MovDelay[x][y] <= 0)
12349       {
12350         RemoveField(x, y);
12351         TEST_DrawLevelField(x, y);
12352
12353         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12354       }
12355     }
12356 #endif
12357
12358 #if DEBUG
12359     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12360     {
12361       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12362       printf("GameActions(): This should never happen!\n");
12363
12364       ChangePage[x][y] = -1;
12365     }
12366 #endif
12367
12368     Stop[x][y] = FALSE;
12369     if (WasJustMoving[x][y] > 0)
12370       WasJustMoving[x][y]--;
12371     if (WasJustFalling[x][y] > 0)
12372       WasJustFalling[x][y]--;
12373     if (CheckCollision[x][y] > 0)
12374       CheckCollision[x][y]--;
12375     if (CheckImpact[x][y] > 0)
12376       CheckImpact[x][y]--;
12377
12378     GfxFrame[x][y]++;
12379
12380     /* reset finished pushing action (not done in ContinueMoving() to allow
12381        continuous pushing animation for elements with zero push delay) */
12382     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12383     {
12384       ResetGfxAnimation(x, y);
12385       TEST_DrawLevelField(x, y);
12386     }
12387
12388 #if DEBUG
12389     if (IS_BLOCKED(x, y))
12390     {
12391       int oldx, oldy;
12392
12393       Blocked2Moving(x, y, &oldx, &oldy);
12394       if (!IS_MOVING(oldx, oldy))
12395       {
12396         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12397         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12398         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12399         printf("GameActions(): This should never happen!\n");
12400       }
12401     }
12402 #endif
12403   }
12404
12405 #if 0
12406   debug_print_timestamp(0, "- time for pre-main loop:");
12407 #endif
12408
12409 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12410   SCAN_PLAYFIELD(x, y)
12411   {
12412     element = Feld[x][y];
12413     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12414
12415 #if 1
12416     {
12417 #if 1
12418       int element2 = element;
12419       int graphic2 = graphic;
12420 #else
12421       int element2 = Feld[x][y];
12422       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12423 #endif
12424       int last_gfx_frame = GfxFrame[x][y];
12425
12426       if (graphic_info[graphic2].anim_global_sync)
12427         GfxFrame[x][y] = FrameCounter;
12428       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12429         GfxFrame[x][y] = CustomValue[x][y];
12430       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12431         GfxFrame[x][y] = element_info[element2].collect_score;
12432       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12433         GfxFrame[x][y] = ChangeDelay[x][y];
12434
12435       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12436         DrawLevelGraphicAnimation(x, y, graphic2);
12437     }
12438 #else
12439     ResetGfxFrame(x, y, TRUE);
12440 #endif
12441
12442 #if 1
12443     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12444         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12445       ResetRandomAnimationValue(x, y);
12446 #endif
12447
12448 #if 1
12449     SetRandomAnimationValue(x, y);
12450 #endif
12451
12452 #if 1
12453     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12454 #endif
12455   }
12456 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12457
12458 #if 0
12459   debug_print_timestamp(0, "- time for TEST loop:     -->");
12460 #endif
12461
12462   SCAN_PLAYFIELD(x, y)
12463   {
12464     element = Feld[x][y];
12465     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12466
12467     ResetGfxFrame(x, y, TRUE);
12468
12469     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12470         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12471       ResetRandomAnimationValue(x, y);
12472
12473     SetRandomAnimationValue(x, y);
12474
12475     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12476
12477     if (IS_INACTIVE(element))
12478     {
12479       if (IS_ANIMATED(graphic))
12480         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12481
12482       continue;
12483     }
12484
12485     /* this may take place after moving, so 'element' may have changed */
12486     if (IS_CHANGING(x, y) &&
12487         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12488     {
12489       int page = element_info[element].event_page_nr[CE_DELAY];
12490
12491 #if 1
12492       HandleElementChange(x, y, page);
12493 #else
12494       if (CAN_CHANGE(element))
12495         HandleElementChange(x, y, page);
12496
12497       if (HAS_ACTION(element))
12498         ExecuteCustomElementAction(x, y, element, page);
12499 #endif
12500
12501       element = Feld[x][y];
12502       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12503     }
12504
12505 #if 0   // ---------------------------------------------------------------------
12506
12507     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12508     {
12509       StartMoving(x, y);
12510
12511       element = Feld[x][y];
12512       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12513
12514       if (IS_ANIMATED(graphic) &&
12515           !IS_MOVING(x, y) &&
12516           !Stop[x][y])
12517         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12518
12519       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12520         TEST_DrawTwinkleOnField(x, y);
12521     }
12522     else if (IS_MOVING(x, y))
12523       ContinueMoving(x, y);
12524     else
12525     {
12526       switch (element)
12527       {
12528         case EL_ACID:
12529         case EL_EXIT_OPEN:
12530         case EL_EM_EXIT_OPEN:
12531         case EL_SP_EXIT_OPEN:
12532         case EL_STEEL_EXIT_OPEN:
12533         case EL_EM_STEEL_EXIT_OPEN:
12534         case EL_SP_TERMINAL:
12535         case EL_SP_TERMINAL_ACTIVE:
12536         case EL_EXTRA_TIME:
12537         case EL_SHIELD_NORMAL:
12538         case EL_SHIELD_DEADLY:
12539           if (IS_ANIMATED(graphic))
12540             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12541           break;
12542
12543         case EL_DYNAMITE_ACTIVE:
12544         case EL_EM_DYNAMITE_ACTIVE:
12545         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12546         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12547         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12548         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12549         case EL_SP_DISK_RED_ACTIVE:
12550           CheckDynamite(x, y);
12551           break;
12552
12553         case EL_AMOEBA_GROWING:
12554           AmoebeWaechst(x, y);
12555           break;
12556
12557         case EL_AMOEBA_SHRINKING:
12558           AmoebaDisappearing(x, y);
12559           break;
12560
12561 #if !USE_NEW_AMOEBA_CODE
12562         case EL_AMOEBA_WET:
12563         case EL_AMOEBA_DRY:
12564         case EL_AMOEBA_FULL:
12565         case EL_BD_AMOEBA:
12566         case EL_EMC_DRIPPER:
12567           AmoebeAbleger(x, y);
12568           break;
12569 #endif
12570
12571         case EL_GAME_OF_LIFE:
12572         case EL_BIOMAZE:
12573           Life(x, y);
12574           break;
12575
12576         case EL_EXIT_CLOSED:
12577           CheckExit(x, y);
12578           break;
12579
12580         case EL_EM_EXIT_CLOSED:
12581           CheckExitEM(x, y);
12582           break;
12583
12584         case EL_STEEL_EXIT_CLOSED:
12585           CheckExitSteel(x, y);
12586           break;
12587
12588         case EL_EM_STEEL_EXIT_CLOSED:
12589           CheckExitSteelEM(x, y);
12590           break;
12591
12592         case EL_SP_EXIT_CLOSED:
12593           CheckExitSP(x, y);
12594           break;
12595
12596         case EL_EXPANDABLE_WALL_GROWING:
12597         case EL_EXPANDABLE_STEELWALL_GROWING:
12598           MauerWaechst(x, y);
12599           break;
12600
12601         case EL_EXPANDABLE_WALL:
12602         case EL_EXPANDABLE_WALL_HORIZONTAL:
12603         case EL_EXPANDABLE_WALL_VERTICAL:
12604         case EL_EXPANDABLE_WALL_ANY:
12605         case EL_BD_EXPANDABLE_WALL:
12606           MauerAbleger(x, y);
12607           break;
12608
12609         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12610         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12611         case EL_EXPANDABLE_STEELWALL_ANY:
12612           MauerAblegerStahl(x, y);
12613           break;
12614
12615         case EL_FLAMES:
12616           CheckForDragon(x, y);
12617           break;
12618
12619         case EL_EXPLOSION:
12620           break;
12621
12622         case EL_ELEMENT_SNAPPING:
12623         case EL_DIAGONAL_SHRINKING:
12624         case EL_DIAGONAL_GROWING:
12625         {
12626           graphic =
12627             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12628
12629           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12630           break;
12631         }
12632
12633         default:
12634           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12635             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12636           break;
12637       }
12638     }
12639
12640 #else   // ---------------------------------------------------------------------
12641
12642     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12643     {
12644       StartMoving(x, y);
12645
12646       element = Feld[x][y];
12647       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12648
12649       if (IS_ANIMATED(graphic) &&
12650           !IS_MOVING(x, y) &&
12651           !Stop[x][y])
12652         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12653
12654       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12655         TEST_DrawTwinkleOnField(x, y);
12656     }
12657     else if ((element == EL_ACID ||
12658               element == EL_EXIT_OPEN ||
12659               element == EL_EM_EXIT_OPEN ||
12660               element == EL_SP_EXIT_OPEN ||
12661               element == EL_STEEL_EXIT_OPEN ||
12662               element == EL_EM_STEEL_EXIT_OPEN ||
12663               element == EL_SP_TERMINAL ||
12664               element == EL_SP_TERMINAL_ACTIVE ||
12665               element == EL_EXTRA_TIME ||
12666               element == EL_SHIELD_NORMAL ||
12667               element == EL_SHIELD_DEADLY) &&
12668              IS_ANIMATED(graphic))
12669       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12670     else if (IS_MOVING(x, y))
12671       ContinueMoving(x, y);
12672     else if (IS_ACTIVE_BOMB(element))
12673       CheckDynamite(x, y);
12674     else if (element == EL_AMOEBA_GROWING)
12675       AmoebeWaechst(x, y);
12676     else if (element == EL_AMOEBA_SHRINKING)
12677       AmoebaDisappearing(x, y);
12678
12679 #if !USE_NEW_AMOEBA_CODE
12680     else if (IS_AMOEBALIVE(element))
12681       AmoebeAbleger(x, y);
12682 #endif
12683
12684     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12685       Life(x, y);
12686     else if (element == EL_EXIT_CLOSED)
12687       CheckExit(x, y);
12688     else if (element == EL_EM_EXIT_CLOSED)
12689       CheckExitEM(x, y);
12690     else if (element == EL_STEEL_EXIT_CLOSED)
12691       CheckExitSteel(x, y);
12692     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12693       CheckExitSteelEM(x, y);
12694     else if (element == EL_SP_EXIT_CLOSED)
12695       CheckExitSP(x, y);
12696     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12697              element == EL_EXPANDABLE_STEELWALL_GROWING)
12698       MauerWaechst(x, y);
12699     else if (element == EL_EXPANDABLE_WALL ||
12700              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12701              element == EL_EXPANDABLE_WALL_VERTICAL ||
12702              element == EL_EXPANDABLE_WALL_ANY ||
12703              element == EL_BD_EXPANDABLE_WALL)
12704       MauerAbleger(x, y);
12705     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12706              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12707              element == EL_EXPANDABLE_STEELWALL_ANY)
12708       MauerAblegerStahl(x, y);
12709     else if (element == EL_FLAMES)
12710       CheckForDragon(x, y);
12711     else if (element == EL_EXPLOSION)
12712       ; /* drawing of correct explosion animation is handled separately */
12713     else if (element == EL_ELEMENT_SNAPPING ||
12714              element == EL_DIAGONAL_SHRINKING ||
12715              element == EL_DIAGONAL_GROWING)
12716     {
12717       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12718
12719       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12720     }
12721     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12722       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12723
12724 #endif  // ---------------------------------------------------------------------
12725
12726     if (IS_BELT_ACTIVE(element))
12727       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12728
12729     if (game.magic_wall_active)
12730     {
12731       int jx = local_player->jx, jy = local_player->jy;
12732
12733       /* play the element sound at the position nearest to the player */
12734       if ((element == EL_MAGIC_WALL_FULL ||
12735            element == EL_MAGIC_WALL_ACTIVE ||
12736            element == EL_MAGIC_WALL_EMPTYING ||
12737            element == EL_BD_MAGIC_WALL_FULL ||
12738            element == EL_BD_MAGIC_WALL_ACTIVE ||
12739            element == EL_BD_MAGIC_WALL_EMPTYING ||
12740            element == EL_DC_MAGIC_WALL_FULL ||
12741            element == EL_DC_MAGIC_WALL_ACTIVE ||
12742            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12743           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12744       {
12745         magic_wall_x = x;
12746         magic_wall_y = y;
12747       }
12748     }
12749   }
12750
12751 #if 0
12752   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12753 #endif
12754
12755 #if USE_NEW_AMOEBA_CODE
12756   /* new experimental amoeba growth stuff */
12757   if (!(FrameCounter % 8))
12758   {
12759     static unsigned long random = 1684108901;
12760
12761     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12762     {
12763       x = RND(lev_fieldx);
12764       y = RND(lev_fieldy);
12765       element = Feld[x][y];
12766
12767       if (!IS_PLAYER(x,y) &&
12768           (element == EL_EMPTY ||
12769            CAN_GROW_INTO(element) ||
12770            element == EL_QUICKSAND_EMPTY ||
12771            element == EL_QUICKSAND_FAST_EMPTY ||
12772            element == EL_ACID_SPLASH_LEFT ||
12773            element == EL_ACID_SPLASH_RIGHT))
12774       {
12775         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12776             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12777             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12778             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12779           Feld[x][y] = EL_AMOEBA_DROP;
12780       }
12781
12782       random = random * 129 + 1;
12783     }
12784   }
12785 #endif
12786
12787 #if 0
12788   if (game.explosions_delayed)
12789 #endif
12790   {
12791     game.explosions_delayed = FALSE;
12792
12793     SCAN_PLAYFIELD(x, y)
12794     {
12795       element = Feld[x][y];
12796
12797       if (ExplodeField[x][y])
12798         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12799       else if (element == EL_EXPLOSION)
12800         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12801
12802       ExplodeField[x][y] = EX_TYPE_NONE;
12803     }
12804
12805     game.explosions_delayed = TRUE;
12806   }
12807
12808   if (game.magic_wall_active)
12809   {
12810     if (!(game.magic_wall_time_left % 4))
12811     {
12812       int element = Feld[magic_wall_x][magic_wall_y];
12813
12814       if (element == EL_BD_MAGIC_WALL_FULL ||
12815           element == EL_BD_MAGIC_WALL_ACTIVE ||
12816           element == EL_BD_MAGIC_WALL_EMPTYING)
12817         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12818       else if (element == EL_DC_MAGIC_WALL_FULL ||
12819                element == EL_DC_MAGIC_WALL_ACTIVE ||
12820                element == EL_DC_MAGIC_WALL_EMPTYING)
12821         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12822       else
12823         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12824     }
12825
12826     if (game.magic_wall_time_left > 0)
12827     {
12828       game.magic_wall_time_left--;
12829
12830       if (!game.magic_wall_time_left)
12831       {
12832         SCAN_PLAYFIELD(x, y)
12833         {
12834           element = Feld[x][y];
12835
12836           if (element == EL_MAGIC_WALL_ACTIVE ||
12837               element == EL_MAGIC_WALL_FULL)
12838           {
12839             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12840             TEST_DrawLevelField(x, y);
12841           }
12842           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12843                    element == EL_BD_MAGIC_WALL_FULL)
12844           {
12845             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12846             TEST_DrawLevelField(x, y);
12847           }
12848           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12849                    element == EL_DC_MAGIC_WALL_FULL)
12850           {
12851             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12852             TEST_DrawLevelField(x, y);
12853           }
12854         }
12855
12856         game.magic_wall_active = FALSE;
12857       }
12858     }
12859   }
12860
12861   if (game.light_time_left > 0)
12862   {
12863     game.light_time_left--;
12864
12865     if (game.light_time_left == 0)
12866       RedrawAllLightSwitchesAndInvisibleElements();
12867   }
12868
12869   if (game.timegate_time_left > 0)
12870   {
12871     game.timegate_time_left--;
12872
12873     if (game.timegate_time_left == 0)
12874       CloseAllOpenTimegates();
12875   }
12876
12877   if (game.lenses_time_left > 0)
12878   {
12879     game.lenses_time_left--;
12880
12881     if (game.lenses_time_left == 0)
12882       RedrawAllInvisibleElementsForLenses();
12883   }
12884
12885   if (game.magnify_time_left > 0)
12886   {
12887     game.magnify_time_left--;
12888
12889     if (game.magnify_time_left == 0)
12890       RedrawAllInvisibleElementsForMagnifier();
12891   }
12892
12893   for (i = 0; i < MAX_PLAYERS; i++)
12894   {
12895     struct PlayerInfo *player = &stored_player[i];
12896
12897     if (SHIELD_ON(player))
12898     {
12899       if (player->shield_deadly_time_left)
12900         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12901       else if (player->shield_normal_time_left)
12902         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12903     }
12904   }
12905
12906 #if USE_DELAYED_GFX_REDRAW
12907   SCAN_PLAYFIELD(x, y)
12908   {
12909 #if 1
12910     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12911 #else
12912     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12913         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12914 #endif
12915     {
12916       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12917          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12918
12919       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12920         DrawLevelField(x, y);
12921
12922       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12923         DrawLevelFieldCrumbledSand(x, y);
12924
12925       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12926         DrawLevelFieldCrumbledSandNeighbours(x, y);
12927
12928       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12929         DrawTwinkleOnField(x, y);
12930     }
12931
12932     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12933   }
12934 #endif
12935
12936   CheckLevelTime();
12937
12938   DrawAllPlayers();
12939   PlayAllPlayersSound();
12940
12941   if (options.debug)                    /* calculate frames per second */
12942   {
12943     static unsigned long fps_counter = 0;
12944     static int fps_frames = 0;
12945     unsigned long fps_delay_ms = Counter() - fps_counter;
12946
12947     fps_frames++;
12948
12949     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12950     {
12951       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12952
12953       fps_frames = 0;
12954       fps_counter = Counter();
12955     }
12956
12957     redraw_mask |= REDRAW_FPS;
12958   }
12959
12960   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12961
12962   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12963   {
12964     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12965
12966     local_player->show_envelope = 0;
12967   }
12968
12969 #if 0
12970   debug_print_timestamp(0, "stop main loop profiling ");
12971   printf("----------------------------------------------------------\n");
12972 #endif
12973
12974   /* use random number generator in every frame to make it less predictable */
12975   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12976     RND(1);
12977 }
12978
12979 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12980 {
12981   int min_x = x, min_y = y, max_x = x, max_y = y;
12982   int i;
12983
12984   for (i = 0; i < MAX_PLAYERS; i++)
12985   {
12986     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12987
12988     if (!stored_player[i].active || &stored_player[i] == player)
12989       continue;
12990
12991     min_x = MIN(min_x, jx);
12992     min_y = MIN(min_y, jy);
12993     max_x = MAX(max_x, jx);
12994     max_y = MAX(max_y, jy);
12995   }
12996
12997   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12998 }
12999
13000 static boolean AllPlayersInVisibleScreen()
13001 {
13002   int i;
13003
13004   for (i = 0; i < MAX_PLAYERS; i++)
13005   {
13006     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13007
13008     if (!stored_player[i].active)
13009       continue;
13010
13011     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13012       return FALSE;
13013   }
13014
13015   return TRUE;
13016 }
13017
13018 void ScrollLevel(int dx, int dy)
13019 {
13020 #if 0
13021   /* (directly solved in BlitBitmap() now) */
13022   static Bitmap *bitmap_db_field2 = NULL;
13023   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13024   int x, y;
13025 #else
13026   int x, y;
13027 #endif
13028
13029 #if 0
13030   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13031   /* only horizontal XOR vertical scroll direction allowed */
13032   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13033     return;
13034 #endif
13035
13036 #if 0
13037   /* (directly solved in BlitBitmap() now) */
13038   if (bitmap_db_field2 == NULL)
13039     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13040
13041   /* needed when blitting directly to same bitmap -- should not be needed with
13042      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13043   BlitBitmap(drawto_field, bitmap_db_field2,
13044              FX + TILEX * (dx == -1) - softscroll_offset,
13045              FY + TILEY * (dy == -1) - softscroll_offset,
13046              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13047              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13048              FX + TILEX * (dx == 1) - softscroll_offset,
13049              FY + TILEY * (dy == 1) - softscroll_offset);
13050   BlitBitmap(bitmap_db_field2, drawto_field,
13051              FX + TILEX * (dx == 1) - softscroll_offset,
13052              FY + TILEY * (dy == 1) - softscroll_offset,
13053              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13054              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13055              FX + TILEX * (dx == 1) - softscroll_offset,
13056              FY + TILEY * (dy == 1) - softscroll_offset);
13057
13058 #else
13059
13060 #if 0
13061   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13062   int xsize = (BX2 - BX1 + 1);
13063   int ysize = (BY2 - BY1 + 1);
13064   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13065   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13066   int step  = (start < end ? +1 : -1);
13067
13068   for (i = start; i != end; i += step)
13069   {
13070     BlitBitmap(drawto_field, drawto_field,
13071                FX + TILEX * (dx != 0 ? i + step : 0),
13072                FY + TILEY * (dy != 0 ? i + step : 0),
13073                TILEX * (dx != 0 ? 1 : xsize),
13074                TILEY * (dy != 0 ? 1 : ysize),
13075                FX + TILEX * (dx != 0 ? i : 0),
13076                FY + TILEY * (dy != 0 ? i : 0));
13077   }
13078
13079 #else
13080
13081   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13082
13083   BlitBitmap(drawto_field, drawto_field,
13084              FX + TILEX * (dx == -1) - softscroll_offset,
13085              FY + TILEY * (dy == -1) - softscroll_offset,
13086              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13087              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13088              FX + TILEX * (dx == 1) - softscroll_offset,
13089              FY + TILEY * (dy == 1) - softscroll_offset);
13090 #endif
13091 #endif
13092
13093   if (dx != 0)
13094   {
13095     x = (dx == 1 ? BX1 : BX2);
13096     for (y = BY1; y <= BY2; y++)
13097       DrawScreenField(x, y);
13098   }
13099
13100   if (dy != 0)
13101   {
13102     y = (dy == 1 ? BY1 : BY2);
13103     for (x = BX1; x <= BX2; x++)
13104       DrawScreenField(x, y);
13105   }
13106
13107   redraw_mask |= REDRAW_FIELD;
13108 }
13109
13110 static boolean canFallDown(struct PlayerInfo *player)
13111 {
13112   int jx = player->jx, jy = player->jy;
13113
13114   return (IN_LEV_FIELD(jx, jy + 1) &&
13115           (IS_FREE(jx, jy + 1) ||
13116            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13117           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13118           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13119 }
13120
13121 static boolean canPassField(int x, int y, int move_dir)
13122 {
13123   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13124   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13125   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13126   int nextx = x + dx;
13127   int nexty = y + dy;
13128   int element = Feld[x][y];
13129
13130   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13131           !CAN_MOVE(element) &&
13132           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13133           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13134           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13135 }
13136
13137 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13138 {
13139   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13140   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13141   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13142   int newx = x + dx;
13143   int newy = y + dy;
13144
13145   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13146           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13147           (IS_DIGGABLE(Feld[newx][newy]) ||
13148            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13149            canPassField(newx, newy, move_dir)));
13150 }
13151
13152 static void CheckGravityMovement(struct PlayerInfo *player)
13153 {
13154 #if USE_PLAYER_GRAVITY
13155   if (player->gravity && !player->programmed_action)
13156 #else
13157   if (game.gravity && !player->programmed_action)
13158 #endif
13159   {
13160     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13161     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13162     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13163     int jx = player->jx, jy = player->jy;
13164     boolean player_is_moving_to_valid_field =
13165       (!player_is_snapping &&
13166        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13167         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13168     boolean player_can_fall_down = canFallDown(player);
13169
13170     if (player_can_fall_down &&
13171         !player_is_moving_to_valid_field)
13172       player->programmed_action = MV_DOWN;
13173   }
13174 }
13175
13176 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13177 {
13178   return CheckGravityMovement(player);
13179
13180 #if USE_PLAYER_GRAVITY
13181   if (player->gravity && !player->programmed_action)
13182 #else
13183   if (game.gravity && !player->programmed_action)
13184 #endif
13185   {
13186     int jx = player->jx, jy = player->jy;
13187     boolean field_under_player_is_free =
13188       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13189     boolean player_is_standing_on_valid_field =
13190       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13191        (IS_WALKABLE(Feld[jx][jy]) &&
13192         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13193
13194     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13195       player->programmed_action = MV_DOWN;
13196   }
13197 }
13198
13199 /*
13200   MovePlayerOneStep()
13201   -----------------------------------------------------------------------------
13202   dx, dy:               direction (non-diagonal) to try to move the player to
13203   real_dx, real_dy:     direction as read from input device (can be diagonal)
13204 */
13205
13206 boolean MovePlayerOneStep(struct PlayerInfo *player,
13207                           int dx, int dy, int real_dx, int real_dy)
13208 {
13209   int jx = player->jx, jy = player->jy;
13210   int new_jx = jx + dx, new_jy = jy + dy;
13211 #if !USE_FIXED_DONT_RUN_INTO
13212   int element;
13213 #endif
13214   int can_move;
13215   boolean player_can_move = !player->cannot_move;
13216
13217   if (!player->active || (!dx && !dy))
13218     return MP_NO_ACTION;
13219
13220   player->MovDir = (dx < 0 ? MV_LEFT :
13221                     dx > 0 ? MV_RIGHT :
13222                     dy < 0 ? MV_UP :
13223                     dy > 0 ? MV_DOWN :  MV_NONE);
13224
13225   if (!IN_LEV_FIELD(new_jx, new_jy))
13226     return MP_NO_ACTION;
13227
13228   if (!player_can_move)
13229   {
13230     if (player->MovPos == 0)
13231     {
13232       player->is_moving = FALSE;
13233       player->is_digging = FALSE;
13234       player->is_collecting = FALSE;
13235       player->is_snapping = FALSE;
13236       player->is_pushing = FALSE;
13237     }
13238   }
13239
13240 #if 1
13241   if (!options.network && game.centered_player_nr == -1 &&
13242       !AllPlayersInSight(player, new_jx, new_jy))
13243     return MP_NO_ACTION;
13244 #else
13245   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13246     return MP_NO_ACTION;
13247 #endif
13248
13249 #if !USE_FIXED_DONT_RUN_INTO
13250   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13251
13252   /* (moved to DigField()) */
13253   if (player_can_move && DONT_RUN_INTO(element))
13254   {
13255     if (element == EL_ACID && dx == 0 && dy == 1)
13256     {
13257       SplashAcid(new_jx, new_jy);
13258       Feld[jx][jy] = EL_PLAYER_1;
13259       InitMovingField(jx, jy, MV_DOWN);
13260       Store[jx][jy] = EL_ACID;
13261       ContinueMoving(jx, jy);
13262       BuryPlayer(player);
13263     }
13264     else
13265       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13266
13267     return MP_MOVING;
13268   }
13269 #endif
13270
13271   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13272   if (can_move != MP_MOVING)
13273     return can_move;
13274
13275   /* check if DigField() has caused relocation of the player */
13276   if (player->jx != jx || player->jy != jy)
13277     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13278
13279   StorePlayer[jx][jy] = 0;
13280   player->last_jx = jx;
13281   player->last_jy = jy;
13282   player->jx = new_jx;
13283   player->jy = new_jy;
13284   StorePlayer[new_jx][new_jy] = player->element_nr;
13285
13286   if (player->move_delay_value_next != -1)
13287   {
13288     player->move_delay_value = player->move_delay_value_next;
13289     player->move_delay_value_next = -1;
13290   }
13291
13292   player->MovPos =
13293     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13294
13295   player->step_counter++;
13296
13297   PlayerVisit[jx][jy] = FrameCounter;
13298
13299 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13300   player->is_moving = TRUE;
13301 #endif
13302
13303 #if 1
13304   /* should better be called in MovePlayer(), but this breaks some tapes */
13305   ScrollPlayer(player, SCROLL_INIT);
13306 #endif
13307
13308   return MP_MOVING;
13309 }
13310
13311 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13312 {
13313   int jx = player->jx, jy = player->jy;
13314   int old_jx = jx, old_jy = jy;
13315   int moved = MP_NO_ACTION;
13316
13317   if (!player->active)
13318     return FALSE;
13319
13320   if (!dx && !dy)
13321   {
13322     if (player->MovPos == 0)
13323     {
13324       player->is_moving = FALSE;
13325       player->is_digging = FALSE;
13326       player->is_collecting = FALSE;
13327       player->is_snapping = FALSE;
13328       player->is_pushing = FALSE;
13329     }
13330
13331     return FALSE;
13332   }
13333
13334   if (player->move_delay > 0)
13335     return FALSE;
13336
13337   player->move_delay = -1;              /* set to "uninitialized" value */
13338
13339   /* store if player is automatically moved to next field */
13340   player->is_auto_moving = (player->programmed_action != MV_NONE);
13341
13342   /* remove the last programmed player action */
13343   player->programmed_action = 0;
13344
13345   if (player->MovPos)
13346   {
13347     /* should only happen if pre-1.2 tape recordings are played */
13348     /* this is only for backward compatibility */
13349
13350     int original_move_delay_value = player->move_delay_value;
13351
13352 #if DEBUG
13353     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13354            tape.counter);
13355 #endif
13356
13357     /* scroll remaining steps with finest movement resolution */
13358     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13359
13360     while (player->MovPos)
13361     {
13362       ScrollPlayer(player, SCROLL_GO_ON);
13363       ScrollScreen(NULL, SCROLL_GO_ON);
13364
13365       AdvanceFrameAndPlayerCounters(player->index_nr);
13366
13367       DrawAllPlayers();
13368       BackToFront();
13369     }
13370
13371     player->move_delay_value = original_move_delay_value;
13372   }
13373
13374   player->is_active = FALSE;
13375
13376   if (player->last_move_dir & MV_HORIZONTAL)
13377   {
13378     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13379       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13380   }
13381   else
13382   {
13383     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13384       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13385   }
13386
13387 #if USE_FIXED_BORDER_RUNNING_GFX
13388   if (!moved && !player->is_active)
13389   {
13390     player->is_moving = FALSE;
13391     player->is_digging = FALSE;
13392     player->is_collecting = FALSE;
13393     player->is_snapping = FALSE;
13394     player->is_pushing = FALSE;
13395   }
13396 #endif
13397
13398   jx = player->jx;
13399   jy = player->jy;
13400
13401 #if 1
13402   if (moved & MP_MOVING && !ScreenMovPos &&
13403       (player->index_nr == game.centered_player_nr ||
13404        game.centered_player_nr == -1))
13405 #else
13406   if (moved & MP_MOVING && !ScreenMovPos &&
13407       (player == local_player || !options.network))
13408 #endif
13409   {
13410     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13411     int offset = game.scroll_delay_value;
13412
13413     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13414     {
13415       /* actual player has left the screen -- scroll in that direction */
13416       if (jx != old_jx)         /* player has moved horizontally */
13417         scroll_x += (jx - old_jx);
13418       else                      /* player has moved vertically */
13419         scroll_y += (jy - old_jy);
13420     }
13421     else
13422     {
13423       if (jx != old_jx)         /* player has moved horizontally */
13424       {
13425         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13426             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13427           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13428
13429         /* don't scroll over playfield boundaries */
13430         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13431           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13432
13433         /* don't scroll more than one field at a time */
13434         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13435
13436         /* don't scroll against the player's moving direction */
13437         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13438             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13439           scroll_x = old_scroll_x;
13440       }
13441       else                      /* player has moved vertically */
13442       {
13443         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13444             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13445           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13446
13447         /* don't scroll over playfield boundaries */
13448         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13449           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13450
13451         /* don't scroll more than one field at a time */
13452         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13453
13454         /* don't scroll against the player's moving direction */
13455         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13456             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13457           scroll_y = old_scroll_y;
13458       }
13459     }
13460
13461     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13462     {
13463 #if 1
13464       if (!options.network && game.centered_player_nr == -1 &&
13465           !AllPlayersInVisibleScreen())
13466       {
13467         scroll_x = old_scroll_x;
13468         scroll_y = old_scroll_y;
13469       }
13470       else
13471 #else
13472       if (!options.network && !AllPlayersInVisibleScreen())
13473       {
13474         scroll_x = old_scroll_x;
13475         scroll_y = old_scroll_y;
13476       }
13477       else
13478 #endif
13479       {
13480         ScrollScreen(player, SCROLL_INIT);
13481         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13482       }
13483     }
13484   }
13485
13486   player->StepFrame = 0;
13487
13488   if (moved & MP_MOVING)
13489   {
13490     if (old_jx != jx && old_jy == jy)
13491       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13492     else if (old_jx == jx && old_jy != jy)
13493       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13494
13495     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13496
13497     player->last_move_dir = player->MovDir;
13498     player->is_moving = TRUE;
13499     player->is_snapping = FALSE;
13500     player->is_switching = FALSE;
13501     player->is_dropping = FALSE;
13502     player->is_dropping_pressed = FALSE;
13503     player->drop_pressed_delay = 0;
13504
13505 #if 0
13506     /* should better be called here than above, but this breaks some tapes */
13507     ScrollPlayer(player, SCROLL_INIT);
13508 #endif
13509   }
13510   else
13511   {
13512     CheckGravityMovementWhenNotMoving(player);
13513
13514     player->is_moving = FALSE;
13515
13516     /* at this point, the player is allowed to move, but cannot move right now
13517        (e.g. because of something blocking the way) -- ensure that the player
13518        is also allowed to move in the next frame (in old versions before 3.1.1,
13519        the player was forced to wait again for eight frames before next try) */
13520
13521     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13522       player->move_delay = 0;   /* allow direct movement in the next frame */
13523   }
13524
13525   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13526     player->move_delay = player->move_delay_value;
13527
13528   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13529   {
13530     TestIfPlayerTouchesBadThing(jx, jy);
13531     TestIfPlayerTouchesCustomElement(jx, jy);
13532   }
13533
13534   if (!player->active)
13535     RemovePlayer(player);
13536
13537   return moved;
13538 }
13539
13540 void ScrollPlayer(struct PlayerInfo *player, int mode)
13541 {
13542   int jx = player->jx, jy = player->jy;
13543   int last_jx = player->last_jx, last_jy = player->last_jy;
13544   int move_stepsize = TILEX / player->move_delay_value;
13545
13546 #if USE_NEW_PLAYER_SPEED
13547   if (!player->active)
13548     return;
13549
13550   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13551     return;
13552 #else
13553   if (!player->active || player->MovPos == 0)
13554     return;
13555 #endif
13556
13557   if (mode == SCROLL_INIT)
13558   {
13559     player->actual_frame_counter = FrameCounter;
13560     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13561
13562     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13563         Feld[last_jx][last_jy] == EL_EMPTY)
13564     {
13565       int last_field_block_delay = 0;   /* start with no blocking at all */
13566       int block_delay_adjustment = player->block_delay_adjustment;
13567
13568       /* if player blocks last field, add delay for exactly one move */
13569       if (player->block_last_field)
13570       {
13571         last_field_block_delay += player->move_delay_value;
13572
13573         /* when blocking enabled, prevent moving up despite gravity */
13574 #if USE_PLAYER_GRAVITY
13575         if (player->gravity && player->MovDir == MV_UP)
13576           block_delay_adjustment = -1;
13577 #else
13578         if (game.gravity && player->MovDir == MV_UP)
13579           block_delay_adjustment = -1;
13580 #endif
13581       }
13582
13583       /* add block delay adjustment (also possible when not blocking) */
13584       last_field_block_delay += block_delay_adjustment;
13585
13586       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13587       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13588     }
13589
13590 #if USE_NEW_PLAYER_SPEED
13591     if (player->MovPos != 0)    /* player has not yet reached destination */
13592       return;
13593 #else
13594     return;
13595 #endif
13596   }
13597   else if (!FrameReached(&player->actual_frame_counter, 1))
13598     return;
13599
13600 #if USE_NEW_PLAYER_SPEED
13601   if (player->MovPos != 0)
13602   {
13603     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13604     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13605
13606     /* before DrawPlayer() to draw correct player graphic for this case */
13607     if (player->MovPos == 0)
13608       CheckGravityMovement(player);
13609   }
13610 #else
13611   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13612   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13613
13614   /* before DrawPlayer() to draw correct player graphic for this case */
13615   if (player->MovPos == 0)
13616     CheckGravityMovement(player);
13617 #endif
13618
13619   if (player->MovPos == 0)      /* player reached destination field */
13620   {
13621     if (player->move_delay_reset_counter > 0)
13622     {
13623       player->move_delay_reset_counter--;
13624
13625       if (player->move_delay_reset_counter == 0)
13626       {
13627         /* continue with normal speed after quickly moving through gate */
13628         HALVE_PLAYER_SPEED(player);
13629
13630         /* be able to make the next move without delay */
13631         player->move_delay = 0;
13632       }
13633     }
13634
13635     player->last_jx = jx;
13636     player->last_jy = jy;
13637
13638     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13639         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13640 #if 1
13641         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13642 #endif
13643         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13644         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13645 #if 1
13646         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13647 #endif
13648         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13649         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13650     {
13651       DrawPlayer(player);       /* needed here only to cleanup last field */
13652       RemovePlayer(player);
13653
13654       if (local_player->friends_still_needed == 0 ||
13655           IS_SP_ELEMENT(Feld[jx][jy]))
13656         PlayerWins(player);
13657     }
13658
13659     /* this breaks one level: "machine", level 000 */
13660     {
13661       int move_direction = player->MovDir;
13662       int enter_side = MV_DIR_OPPOSITE(move_direction);
13663       int leave_side = move_direction;
13664       int old_jx = last_jx;
13665       int old_jy = last_jy;
13666       int old_element = Feld[old_jx][old_jy];
13667       int new_element = Feld[jx][jy];
13668
13669       if (IS_CUSTOM_ELEMENT(old_element))
13670         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13671                                    CE_LEFT_BY_PLAYER,
13672                                    player->index_bit, leave_side);
13673
13674       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13675                                           CE_PLAYER_LEAVES_X,
13676                                           player->index_bit, leave_side);
13677
13678       if (IS_CUSTOM_ELEMENT(new_element))
13679         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13680                                    player->index_bit, enter_side);
13681
13682       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13683                                           CE_PLAYER_ENTERS_X,
13684                                           player->index_bit, enter_side);
13685
13686 #if USE_FIX_CE_ACTION_WITH_PLAYER
13687       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13688                                         CE_MOVE_OF_X, move_direction);
13689 #else
13690       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13691                                         CE_MOVE_OF_X, move_direction);
13692 #endif
13693     }
13694
13695     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13696     {
13697       TestIfPlayerTouchesBadThing(jx, jy);
13698       TestIfPlayerTouchesCustomElement(jx, jy);
13699
13700       /* needed because pushed element has not yet reached its destination,
13701          so it would trigger a change event at its previous field location */
13702       if (!player->is_pushing)
13703         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13704
13705       if (!player->active)
13706         RemovePlayer(player);
13707     }
13708
13709     if (!local_player->LevelSolved && level.use_step_counter)
13710     {
13711       int i;
13712
13713       TimePlayed++;
13714
13715       if (TimeLeft > 0)
13716       {
13717         TimeLeft--;
13718
13719         if (TimeLeft <= 10 && setup.time_limit)
13720           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13721
13722 #if 1
13723         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13724
13725         DisplayGameControlValues();
13726 #else
13727         DrawGameValue_Time(TimeLeft);
13728 #endif
13729
13730         if (!TimeLeft && setup.time_limit)
13731           for (i = 0; i < MAX_PLAYERS; i++)
13732             KillPlayer(&stored_player[i]);
13733       }
13734 #if 1
13735       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13736       {
13737         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13738
13739         DisplayGameControlValues();
13740       }
13741 #else
13742       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13743         DrawGameValue_Time(TimePlayed);
13744 #endif
13745     }
13746
13747     if (tape.single_step && tape.recording && !tape.pausing &&
13748         !player->programmed_action)
13749       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13750   }
13751 }
13752
13753 void ScrollScreen(struct PlayerInfo *player, int mode)
13754 {
13755   static unsigned long screen_frame_counter = 0;
13756
13757   if (mode == SCROLL_INIT)
13758   {
13759     /* set scrolling step size according to actual player's moving speed */
13760     ScrollStepSize = TILEX / player->move_delay_value;
13761
13762     screen_frame_counter = FrameCounter;
13763     ScreenMovDir = player->MovDir;
13764     ScreenMovPos = player->MovPos;
13765     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13766     return;
13767   }
13768   else if (!FrameReached(&screen_frame_counter, 1))
13769     return;
13770
13771   if (ScreenMovPos)
13772   {
13773     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13774     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13775     redraw_mask |= REDRAW_FIELD;
13776   }
13777   else
13778     ScreenMovDir = MV_NONE;
13779 }
13780
13781 void TestIfPlayerTouchesCustomElement(int x, int y)
13782 {
13783   static int xy[4][2] =
13784   {
13785     { 0, -1 },
13786     { -1, 0 },
13787     { +1, 0 },
13788     { 0, +1 }
13789   };
13790   static int trigger_sides[4][2] =
13791   {
13792     /* center side       border side */
13793     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13794     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13795     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13796     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13797   };
13798   static int touch_dir[4] =
13799   {
13800     MV_LEFT | MV_RIGHT,
13801     MV_UP   | MV_DOWN,
13802     MV_UP   | MV_DOWN,
13803     MV_LEFT | MV_RIGHT
13804   };
13805   int center_element = Feld[x][y];      /* should always be non-moving! */
13806   int i;
13807
13808   for (i = 0; i < NUM_DIRECTIONS; i++)
13809   {
13810     int xx = x + xy[i][0];
13811     int yy = y + xy[i][1];
13812     int center_side = trigger_sides[i][0];
13813     int border_side = trigger_sides[i][1];
13814     int border_element;
13815
13816     if (!IN_LEV_FIELD(xx, yy))
13817       continue;
13818
13819     if (IS_PLAYER(x, y))                /* player found at center element */
13820     {
13821       struct PlayerInfo *player = PLAYERINFO(x, y);
13822
13823       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13824         border_element = Feld[xx][yy];          /* may be moving! */
13825       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13826         border_element = Feld[xx][yy];
13827       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13828         border_element = MovingOrBlocked2Element(xx, yy);
13829       else
13830         continue;               /* center and border element do not touch */
13831
13832       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13833                                  player->index_bit, border_side);
13834       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13835                                           CE_PLAYER_TOUCHES_X,
13836                                           player->index_bit, border_side);
13837
13838 #if USE_FIX_CE_ACTION_WITH_PLAYER
13839       {
13840         /* use player element that is initially defined in the level playfield,
13841            not the player element that corresponds to the runtime player number
13842            (example: a level that contains EL_PLAYER_3 as the only player would
13843            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13844         int player_element = PLAYERINFO(x, y)->initial_element;
13845
13846         CheckElementChangeBySide(xx, yy, border_element, player_element,
13847                                  CE_TOUCHING_X, border_side);
13848       }
13849 #endif
13850     }
13851     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13852     {
13853       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13854
13855       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13856       {
13857         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13858           continue;             /* center and border element do not touch */
13859       }
13860
13861       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13862                                  player->index_bit, center_side);
13863       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13864                                           CE_PLAYER_TOUCHES_X,
13865                                           player->index_bit, center_side);
13866
13867 #if USE_FIX_CE_ACTION_WITH_PLAYER
13868       {
13869         /* use player element that is initially defined in the level playfield,
13870            not the player element that corresponds to the runtime player number
13871            (example: a level that contains EL_PLAYER_3 as the only player would
13872            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13873         int player_element = PLAYERINFO(xx, yy)->initial_element;
13874
13875         CheckElementChangeBySide(x, y, center_element, player_element,
13876                                  CE_TOUCHING_X, center_side);
13877       }
13878 #endif
13879
13880       break;
13881     }
13882   }
13883 }
13884
13885 #if USE_ELEMENT_TOUCHING_BUGFIX
13886
13887 void TestIfElementTouchesCustomElement(int x, int y)
13888 {
13889   static int xy[4][2] =
13890   {
13891     { 0, -1 },
13892     { -1, 0 },
13893     { +1, 0 },
13894     { 0, +1 }
13895   };
13896   static int trigger_sides[4][2] =
13897   {
13898     /* center side      border side */
13899     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13900     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13901     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13902     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13903   };
13904   static int touch_dir[4] =
13905   {
13906     MV_LEFT | MV_RIGHT,
13907     MV_UP   | MV_DOWN,
13908     MV_UP   | MV_DOWN,
13909     MV_LEFT | MV_RIGHT
13910   };
13911   boolean change_center_element = FALSE;
13912   int center_element = Feld[x][y];      /* should always be non-moving! */
13913   int border_element_old[NUM_DIRECTIONS];
13914   int i;
13915
13916   for (i = 0; i < NUM_DIRECTIONS; i++)
13917   {
13918     int xx = x + xy[i][0];
13919     int yy = y + xy[i][1];
13920     int border_element;
13921
13922     border_element_old[i] = -1;
13923
13924     if (!IN_LEV_FIELD(xx, yy))
13925       continue;
13926
13927     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13928       border_element = Feld[xx][yy];    /* may be moving! */
13929     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13930       border_element = Feld[xx][yy];
13931     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13932       border_element = MovingOrBlocked2Element(xx, yy);
13933     else
13934       continue;                 /* center and border element do not touch */
13935
13936     border_element_old[i] = border_element;
13937   }
13938
13939   for (i = 0; i < NUM_DIRECTIONS; i++)
13940   {
13941     int xx = x + xy[i][0];
13942     int yy = y + xy[i][1];
13943     int center_side = trigger_sides[i][0];
13944     int border_element = border_element_old[i];
13945
13946     if (border_element == -1)
13947       continue;
13948
13949     /* check for change of border element */
13950     CheckElementChangeBySide(xx, yy, border_element, center_element,
13951                              CE_TOUCHING_X, center_side);
13952
13953     /* (center element cannot be player, so we dont have to check this here) */
13954   }
13955
13956   for (i = 0; i < NUM_DIRECTIONS; i++)
13957   {
13958     int xx = x + xy[i][0];
13959     int yy = y + xy[i][1];
13960     int border_side = trigger_sides[i][1];
13961     int border_element = border_element_old[i];
13962
13963     if (border_element == -1)
13964       continue;
13965
13966     /* check for change of center element (but change it only once) */
13967     if (!change_center_element)
13968       change_center_element =
13969         CheckElementChangeBySide(x, y, center_element, border_element,
13970                                  CE_TOUCHING_X, border_side);
13971
13972 #if USE_FIX_CE_ACTION_WITH_PLAYER
13973     if (IS_PLAYER(xx, yy))
13974     {
13975       /* use player element that is initially defined in the level playfield,
13976          not the player element that corresponds to the runtime player number
13977          (example: a level that contains EL_PLAYER_3 as the only player would
13978          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13979       int player_element = PLAYERINFO(xx, yy)->initial_element;
13980
13981       CheckElementChangeBySide(x, y, center_element, player_element,
13982                                CE_TOUCHING_X, border_side);
13983     }
13984 #endif
13985   }
13986 }
13987
13988 #else
13989
13990 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13991 {
13992   static int xy[4][2] =
13993   {
13994     { 0, -1 },
13995     { -1, 0 },
13996     { +1, 0 },
13997     { 0, +1 }
13998   };
13999   static int trigger_sides[4][2] =
14000   {
14001     /* center side      border side */
14002     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14003     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14004     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14005     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14006   };
14007   static int touch_dir[4] =
14008   {
14009     MV_LEFT | MV_RIGHT,
14010     MV_UP   | MV_DOWN,
14011     MV_UP   | MV_DOWN,
14012     MV_LEFT | MV_RIGHT
14013   };
14014   boolean change_center_element = FALSE;
14015   int center_element = Feld[x][y];      /* should always be non-moving! */
14016   int i;
14017
14018   for (i = 0; i < NUM_DIRECTIONS; i++)
14019   {
14020     int xx = x + xy[i][0];
14021     int yy = y + xy[i][1];
14022     int center_side = trigger_sides[i][0];
14023     int border_side = trigger_sides[i][1];
14024     int border_element;
14025
14026     if (!IN_LEV_FIELD(xx, yy))
14027       continue;
14028
14029     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14030       border_element = Feld[xx][yy];    /* may be moving! */
14031     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14032       border_element = Feld[xx][yy];
14033     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14034       border_element = MovingOrBlocked2Element(xx, yy);
14035     else
14036       continue;                 /* center and border element do not touch */
14037
14038     /* check for change of center element (but change it only once) */
14039     if (!change_center_element)
14040       change_center_element =
14041         CheckElementChangeBySide(x, y, center_element, border_element,
14042                                  CE_TOUCHING_X, border_side);
14043
14044     /* check for change of border element */
14045     CheckElementChangeBySide(xx, yy, border_element, center_element,
14046                              CE_TOUCHING_X, center_side);
14047   }
14048 }
14049
14050 #endif
14051
14052 void TestIfElementHitsCustomElement(int x, int y, int direction)
14053 {
14054   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14055   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14056   int hitx = x + dx, hity = y + dy;
14057   int hitting_element = Feld[x][y];
14058   int touched_element;
14059
14060   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14061     return;
14062
14063   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14064                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14065
14066   if (IN_LEV_FIELD(hitx, hity))
14067   {
14068     int opposite_direction = MV_DIR_OPPOSITE(direction);
14069     int hitting_side = direction;
14070     int touched_side = opposite_direction;
14071     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14072                           MovDir[hitx][hity] != direction ||
14073                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14074
14075     object_hit = TRUE;
14076
14077     if (object_hit)
14078     {
14079       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14080                                CE_HITTING_X, touched_side);
14081
14082       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14083                                CE_HIT_BY_X, hitting_side);
14084
14085       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14086                                CE_HIT_BY_SOMETHING, opposite_direction);
14087
14088 #if USE_FIX_CE_ACTION_WITH_PLAYER
14089       if (IS_PLAYER(hitx, hity))
14090       {
14091         /* use player element that is initially defined in the level playfield,
14092            not the player element that corresponds to the runtime player number
14093            (example: a level that contains EL_PLAYER_3 as the only player would
14094            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14095         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14096
14097         CheckElementChangeBySide(x, y, hitting_element, player_element,
14098                                  CE_HITTING_X, touched_side);
14099       }
14100 #endif
14101     }
14102   }
14103
14104   /* "hitting something" is also true when hitting the playfield border */
14105   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14106                            CE_HITTING_SOMETHING, direction);
14107 }
14108
14109 #if 0
14110 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14111 {
14112   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14113   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14114   int hitx = x + dx, hity = y + dy;
14115   int hitting_element = Feld[x][y];
14116   int touched_element;
14117 #if 0
14118   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14119                         !IS_FREE(hitx, hity) &&
14120                         (!IS_MOVING(hitx, hity) ||
14121                          MovDir[hitx][hity] != direction ||
14122                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14123 #endif
14124
14125   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14126     return;
14127
14128 #if 0
14129   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14130     return;
14131 #endif
14132
14133   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14134                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14135
14136   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14137                            EP_CAN_SMASH_EVERYTHING, direction);
14138
14139   if (IN_LEV_FIELD(hitx, hity))
14140   {
14141     int opposite_direction = MV_DIR_OPPOSITE(direction);
14142     int hitting_side = direction;
14143     int touched_side = opposite_direction;
14144 #if 0
14145     int touched_element = MovingOrBlocked2Element(hitx, hity);
14146 #endif
14147 #if 1
14148     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14149                           MovDir[hitx][hity] != direction ||
14150                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14151
14152     object_hit = TRUE;
14153 #endif
14154
14155     if (object_hit)
14156     {
14157       int i;
14158
14159       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14160                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14161
14162       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14163                                CE_OTHER_IS_SMASHING, touched_side);
14164
14165       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14166                                CE_OTHER_GETS_SMASHED, hitting_side);
14167     }
14168   }
14169 }
14170 #endif
14171
14172 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14173 {
14174   int i, kill_x = -1, kill_y = -1;
14175
14176   int bad_element = -1;
14177   static int test_xy[4][2] =
14178   {
14179     { 0, -1 },
14180     { -1, 0 },
14181     { +1, 0 },
14182     { 0, +1 }
14183   };
14184   static int test_dir[4] =
14185   {
14186     MV_UP,
14187     MV_LEFT,
14188     MV_RIGHT,
14189     MV_DOWN
14190   };
14191
14192   for (i = 0; i < NUM_DIRECTIONS; i++)
14193   {
14194     int test_x, test_y, test_move_dir, test_element;
14195
14196     test_x = good_x + test_xy[i][0];
14197     test_y = good_y + test_xy[i][1];
14198
14199     if (!IN_LEV_FIELD(test_x, test_y))
14200       continue;
14201
14202     test_move_dir =
14203       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14204
14205     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14206
14207     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14208        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14209     */
14210     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14211         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14212     {
14213       kill_x = test_x;
14214       kill_y = test_y;
14215       bad_element = test_element;
14216
14217       break;
14218     }
14219   }
14220
14221   if (kill_x != -1 || kill_y != -1)
14222   {
14223     if (IS_PLAYER(good_x, good_y))
14224     {
14225       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14226
14227       if (player->shield_deadly_time_left > 0 &&
14228           !IS_INDESTRUCTIBLE(bad_element))
14229         Bang(kill_x, kill_y);
14230       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14231         KillPlayer(player);
14232     }
14233     else
14234       Bang(good_x, good_y);
14235   }
14236 }
14237
14238 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14239 {
14240   int i, kill_x = -1, kill_y = -1;
14241   int bad_element = Feld[bad_x][bad_y];
14242   static int test_xy[4][2] =
14243   {
14244     { 0, -1 },
14245     { -1, 0 },
14246     { +1, 0 },
14247     { 0, +1 }
14248   };
14249   static int touch_dir[4] =
14250   {
14251     MV_LEFT | MV_RIGHT,
14252     MV_UP   | MV_DOWN,
14253     MV_UP   | MV_DOWN,
14254     MV_LEFT | MV_RIGHT
14255   };
14256   static int test_dir[4] =
14257   {
14258     MV_UP,
14259     MV_LEFT,
14260     MV_RIGHT,
14261     MV_DOWN
14262   };
14263
14264   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14265     return;
14266
14267   for (i = 0; i < NUM_DIRECTIONS; i++)
14268   {
14269     int test_x, test_y, test_move_dir, test_element;
14270
14271     test_x = bad_x + test_xy[i][0];
14272     test_y = bad_y + test_xy[i][1];
14273
14274     if (!IN_LEV_FIELD(test_x, test_y))
14275       continue;
14276
14277     test_move_dir =
14278       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14279
14280     test_element = Feld[test_x][test_y];
14281
14282     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14283        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14284     */
14285     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14286         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14287     {
14288       /* good thing is player or penguin that does not move away */
14289       if (IS_PLAYER(test_x, test_y))
14290       {
14291         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14292
14293         if (bad_element == EL_ROBOT && player->is_moving)
14294           continue;     /* robot does not kill player if he is moving */
14295
14296         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14297         {
14298           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14299             continue;           /* center and border element do not touch */
14300         }
14301
14302         kill_x = test_x;
14303         kill_y = test_y;
14304
14305         break;
14306       }
14307       else if (test_element == EL_PENGUIN)
14308       {
14309         kill_x = test_x;
14310         kill_y = test_y;
14311
14312         break;
14313       }
14314     }
14315   }
14316
14317   if (kill_x != -1 || kill_y != -1)
14318   {
14319     if (IS_PLAYER(kill_x, kill_y))
14320     {
14321       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14322
14323       if (player->shield_deadly_time_left > 0 &&
14324           !IS_INDESTRUCTIBLE(bad_element))
14325         Bang(bad_x, bad_y);
14326       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14327         KillPlayer(player);
14328     }
14329     else
14330       Bang(kill_x, kill_y);
14331   }
14332 }
14333
14334 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14335 {
14336   int bad_element = Feld[bad_x][bad_y];
14337   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14338   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14339   int test_x = bad_x + dx, test_y = bad_y + dy;
14340   int test_move_dir, test_element;
14341   int kill_x = -1, kill_y = -1;
14342
14343   if (!IN_LEV_FIELD(test_x, test_y))
14344     return;
14345
14346   test_move_dir =
14347     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14348
14349   test_element = Feld[test_x][test_y];
14350
14351   if (test_move_dir != bad_move_dir)
14352   {
14353     /* good thing can be player or penguin that does not move away */
14354     if (IS_PLAYER(test_x, test_y))
14355     {
14356       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14357
14358       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14359          player as being hit when he is moving towards the bad thing, because
14360          the "get hit by" condition would be lost after the player stops) */
14361       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14362         return;         /* player moves away from bad thing */
14363
14364       kill_x = test_x;
14365       kill_y = test_y;
14366     }
14367     else if (test_element == EL_PENGUIN)
14368     {
14369       kill_x = test_x;
14370       kill_y = test_y;
14371     }
14372   }
14373
14374   if (kill_x != -1 || kill_y != -1)
14375   {
14376     if (IS_PLAYER(kill_x, kill_y))
14377     {
14378       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14379
14380       if (player->shield_deadly_time_left > 0 &&
14381           !IS_INDESTRUCTIBLE(bad_element))
14382         Bang(bad_x, bad_y);
14383       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14384         KillPlayer(player);
14385     }
14386     else
14387       Bang(kill_x, kill_y);
14388   }
14389 }
14390
14391 void TestIfPlayerTouchesBadThing(int x, int y)
14392 {
14393   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14394 }
14395
14396 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14397 {
14398   TestIfGoodThingHitsBadThing(x, y, move_dir);
14399 }
14400
14401 void TestIfBadThingTouchesPlayer(int x, int y)
14402 {
14403   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14404 }
14405
14406 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14407 {
14408   TestIfBadThingHitsGoodThing(x, y, move_dir);
14409 }
14410
14411 void TestIfFriendTouchesBadThing(int x, int y)
14412 {
14413   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14414 }
14415
14416 void TestIfBadThingTouchesFriend(int x, int y)
14417 {
14418   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14419 }
14420
14421 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14422 {
14423   int i, kill_x = bad_x, kill_y = bad_y;
14424   static int xy[4][2] =
14425   {
14426     { 0, -1 },
14427     { -1, 0 },
14428     { +1, 0 },
14429     { 0, +1 }
14430   };
14431
14432   for (i = 0; i < NUM_DIRECTIONS; i++)
14433   {
14434     int x, y, element;
14435
14436     x = bad_x + xy[i][0];
14437     y = bad_y + xy[i][1];
14438     if (!IN_LEV_FIELD(x, y))
14439       continue;
14440
14441     element = Feld[x][y];
14442     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14443         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14444     {
14445       kill_x = x;
14446       kill_y = y;
14447       break;
14448     }
14449   }
14450
14451   if (kill_x != bad_x || kill_y != bad_y)
14452     Bang(bad_x, bad_y);
14453 }
14454
14455 void KillPlayer(struct PlayerInfo *player)
14456 {
14457   int jx = player->jx, jy = player->jy;
14458
14459   if (!player->active)
14460     return;
14461
14462 #if 0
14463   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14464          player->killed, player->active, player->reanimated);
14465 #endif
14466
14467   /* the following code was introduced to prevent an infinite loop when calling
14468      -> Bang()
14469      -> CheckTriggeredElementChangeExt()
14470      -> ExecuteCustomElementAction()
14471      -> KillPlayer()
14472      -> (infinitely repeating the above sequence of function calls)
14473      which occurs when killing the player while having a CE with the setting
14474      "kill player X when explosion of <player X>"; the solution using a new
14475      field "player->killed" was chosen for backwards compatibility, although
14476      clever use of the fields "player->active" etc. would probably also work */
14477 #if 1
14478   if (player->killed)
14479     return;
14480 #endif
14481
14482   player->killed = TRUE;
14483
14484   /* remove accessible field at the player's position */
14485   Feld[jx][jy] = EL_EMPTY;
14486
14487   /* deactivate shield (else Bang()/Explode() would not work right) */
14488   player->shield_normal_time_left = 0;
14489   player->shield_deadly_time_left = 0;
14490
14491 #if 0
14492   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14493          player->killed, player->active, player->reanimated);
14494 #endif
14495
14496   Bang(jx, jy);
14497
14498 #if 0
14499   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14500          player->killed, player->active, player->reanimated);
14501 #endif
14502
14503 #if USE_PLAYER_REANIMATION
14504 #if 1
14505   if (player->reanimated)       /* killed player may have been reanimated */
14506     player->killed = player->reanimated = FALSE;
14507   else
14508     BuryPlayer(player);
14509 #else
14510   if (player->killed)           /* player may have been reanimated */
14511     BuryPlayer(player);
14512 #endif
14513 #else
14514   BuryPlayer(player);
14515 #endif
14516 }
14517
14518 static void KillPlayerUnlessEnemyProtected(int x, int y)
14519 {
14520   if (!PLAYER_ENEMY_PROTECTED(x, y))
14521     KillPlayer(PLAYERINFO(x, y));
14522 }
14523
14524 static void KillPlayerUnlessExplosionProtected(int x, int y)
14525 {
14526   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14527     KillPlayer(PLAYERINFO(x, y));
14528 }
14529
14530 void BuryPlayer(struct PlayerInfo *player)
14531 {
14532   int jx = player->jx, jy = player->jy;
14533
14534   if (!player->active)
14535     return;
14536
14537   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14538   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14539
14540   player->GameOver = TRUE;
14541   RemovePlayer(player);
14542 }
14543
14544 void RemovePlayer(struct PlayerInfo *player)
14545 {
14546   int jx = player->jx, jy = player->jy;
14547   int i, found = FALSE;
14548
14549   player->present = FALSE;
14550   player->active = FALSE;
14551
14552   if (!ExplodeField[jx][jy])
14553     StorePlayer[jx][jy] = 0;
14554
14555   if (player->is_moving)
14556     TEST_DrawLevelField(player->last_jx, player->last_jy);
14557
14558   for (i = 0; i < MAX_PLAYERS; i++)
14559     if (stored_player[i].active)
14560       found = TRUE;
14561
14562   if (!found)
14563     AllPlayersGone = TRUE;
14564
14565   ExitX = ZX = jx;
14566   ExitY = ZY = jy;
14567 }
14568
14569 #if USE_NEW_SNAP_DELAY
14570 static void setFieldForSnapping(int x, int y, int element, int direction)
14571 {
14572   struct ElementInfo *ei = &element_info[element];
14573   int direction_bit = MV_DIR_TO_BIT(direction);
14574   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14575   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14576                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14577
14578   Feld[x][y] = EL_ELEMENT_SNAPPING;
14579   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14580
14581   ResetGfxAnimation(x, y);
14582
14583   GfxElement[x][y] = element;
14584   GfxAction[x][y] = action;
14585   GfxDir[x][y] = direction;
14586   GfxFrame[x][y] = -1;
14587 }
14588 #endif
14589
14590 /*
14591   =============================================================================
14592   checkDiagonalPushing()
14593   -----------------------------------------------------------------------------
14594   check if diagonal input device direction results in pushing of object
14595   (by checking if the alternative direction is walkable, diggable, ...)
14596   =============================================================================
14597 */
14598
14599 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14600                                     int x, int y, int real_dx, int real_dy)
14601 {
14602   int jx, jy, dx, dy, xx, yy;
14603
14604   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14605     return TRUE;
14606
14607   /* diagonal direction: check alternative direction */
14608   jx = player->jx;
14609   jy = player->jy;
14610   dx = x - jx;
14611   dy = y - jy;
14612   xx = jx + (dx == 0 ? real_dx : 0);
14613   yy = jy + (dy == 0 ? real_dy : 0);
14614
14615   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14616 }
14617
14618 /*
14619   =============================================================================
14620   DigField()
14621   -----------------------------------------------------------------------------
14622   x, y:                 field next to player (non-diagonal) to try to dig to
14623   real_dx, real_dy:     direction as read from input device (can be diagonal)
14624   =============================================================================
14625 */
14626
14627 static int DigField(struct PlayerInfo *player,
14628                     int oldx, int oldy, int x, int y,
14629                     int real_dx, int real_dy, int mode)
14630 {
14631   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14632   boolean player_was_pushing = player->is_pushing;
14633   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14634   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14635   int jx = oldx, jy = oldy;
14636   int dx = x - jx, dy = y - jy;
14637   int nextx = x + dx, nexty = y + dy;
14638   int move_direction = (dx == -1 ? MV_LEFT  :
14639                         dx == +1 ? MV_RIGHT :
14640                         dy == -1 ? MV_UP    :
14641                         dy == +1 ? MV_DOWN  : MV_NONE);
14642   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14643   int dig_side = MV_DIR_OPPOSITE(move_direction);
14644   int old_element = Feld[jx][jy];
14645 #if USE_FIXED_DONT_RUN_INTO
14646   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14647 #else
14648   int element;
14649 #endif
14650   int collect_count;
14651
14652   if (is_player)                /* function can also be called by EL_PENGUIN */
14653   {
14654     if (player->MovPos == 0)
14655     {
14656       player->is_digging = FALSE;
14657       player->is_collecting = FALSE;
14658     }
14659
14660     if (player->MovPos == 0)    /* last pushing move finished */
14661       player->is_pushing = FALSE;
14662
14663     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14664     {
14665       player->is_switching = FALSE;
14666       player->push_delay = -1;
14667
14668       return MP_NO_ACTION;
14669     }
14670   }
14671
14672 #if !USE_FIXED_DONT_RUN_INTO
14673   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14674     return MP_NO_ACTION;
14675 #endif
14676
14677   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14678     old_element = Back[jx][jy];
14679
14680   /* in case of element dropped at player position, check background */
14681   else if (Back[jx][jy] != EL_EMPTY &&
14682            game.engine_version >= VERSION_IDENT(2,2,0,0))
14683     old_element = Back[jx][jy];
14684
14685   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14686     return MP_NO_ACTION;        /* field has no opening in this direction */
14687
14688   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14689     return MP_NO_ACTION;        /* field has no opening in this direction */
14690
14691 #if USE_FIXED_DONT_RUN_INTO
14692   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14693   {
14694     SplashAcid(x, y);
14695
14696     Feld[jx][jy] = player->artwork_element;
14697     InitMovingField(jx, jy, MV_DOWN);
14698     Store[jx][jy] = EL_ACID;
14699     ContinueMoving(jx, jy);
14700     BuryPlayer(player);
14701
14702     return MP_DONT_RUN_INTO;
14703   }
14704 #endif
14705
14706 #if USE_FIXED_DONT_RUN_INTO
14707   if (player_can_move && DONT_RUN_INTO(element))
14708   {
14709     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14710
14711     return MP_DONT_RUN_INTO;
14712   }
14713 #endif
14714
14715 #if USE_FIXED_DONT_RUN_INTO
14716   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14717     return MP_NO_ACTION;
14718 #endif
14719
14720 #if !USE_FIXED_DONT_RUN_INTO
14721   element = Feld[x][y];
14722 #endif
14723
14724   collect_count = element_info[element].collect_count_initial;
14725
14726   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14727     return MP_NO_ACTION;
14728
14729   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14730     player_can_move = player_can_move_or_snap;
14731
14732   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14733       game.engine_version >= VERSION_IDENT(2,2,0,0))
14734   {
14735     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14736                                player->index_bit, dig_side);
14737     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14738                                         player->index_bit, dig_side);
14739
14740     if (element == EL_DC_LANDMINE)
14741       Bang(x, y);
14742
14743     if (Feld[x][y] != element)          /* field changed by snapping */
14744       return MP_ACTION;
14745
14746     return MP_NO_ACTION;
14747   }
14748
14749 #if USE_PLAYER_GRAVITY
14750   if (player->gravity && is_player && !player->is_auto_moving &&
14751       canFallDown(player) && move_direction != MV_DOWN &&
14752       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14753     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14754 #else
14755   if (game.gravity && is_player && !player->is_auto_moving &&
14756       canFallDown(player) && move_direction != MV_DOWN &&
14757       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14758     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14759 #endif
14760
14761   if (player_can_move &&
14762       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14763   {
14764     int sound_element = SND_ELEMENT(element);
14765     int sound_action = ACTION_WALKING;
14766
14767     if (IS_RND_GATE(element))
14768     {
14769       if (!player->key[RND_GATE_NR(element)])
14770         return MP_NO_ACTION;
14771     }
14772     else if (IS_RND_GATE_GRAY(element))
14773     {
14774       if (!player->key[RND_GATE_GRAY_NR(element)])
14775         return MP_NO_ACTION;
14776     }
14777     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14778     {
14779       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14780         return MP_NO_ACTION;
14781     }
14782     else if (element == EL_EXIT_OPEN ||
14783              element == EL_EM_EXIT_OPEN ||
14784 #if 1
14785              element == EL_EM_EXIT_OPENING ||
14786 #endif
14787              element == EL_STEEL_EXIT_OPEN ||
14788              element == EL_EM_STEEL_EXIT_OPEN ||
14789 #if 1
14790              element == EL_EM_STEEL_EXIT_OPENING ||
14791 #endif
14792              element == EL_SP_EXIT_OPEN ||
14793              element == EL_SP_EXIT_OPENING)
14794     {
14795       sound_action = ACTION_PASSING;    /* player is passing exit */
14796     }
14797     else if (element == EL_EMPTY)
14798     {
14799       sound_action = ACTION_MOVING;             /* nothing to walk on */
14800     }
14801
14802     /* play sound from background or player, whatever is available */
14803     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14804       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14805     else
14806       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14807   }
14808   else if (player_can_move &&
14809            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14810   {
14811     if (!ACCESS_FROM(element, opposite_direction))
14812       return MP_NO_ACTION;      /* field not accessible from this direction */
14813
14814     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14815       return MP_NO_ACTION;
14816
14817     if (IS_EM_GATE(element))
14818     {
14819       if (!player->key[EM_GATE_NR(element)])
14820         return MP_NO_ACTION;
14821     }
14822     else if (IS_EM_GATE_GRAY(element))
14823     {
14824       if (!player->key[EM_GATE_GRAY_NR(element)])
14825         return MP_NO_ACTION;
14826     }
14827     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14828     {
14829       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14830         return MP_NO_ACTION;
14831     }
14832     else if (IS_EMC_GATE(element))
14833     {
14834       if (!player->key[EMC_GATE_NR(element)])
14835         return MP_NO_ACTION;
14836     }
14837     else if (IS_EMC_GATE_GRAY(element))
14838     {
14839       if (!player->key[EMC_GATE_GRAY_NR(element)])
14840         return MP_NO_ACTION;
14841     }
14842     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14843     {
14844       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14845         return MP_NO_ACTION;
14846     }
14847     else if (element == EL_DC_GATE_WHITE ||
14848              element == EL_DC_GATE_WHITE_GRAY ||
14849              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14850     {
14851       if (player->num_white_keys == 0)
14852         return MP_NO_ACTION;
14853
14854       player->num_white_keys--;
14855     }
14856     else if (IS_SP_PORT(element))
14857     {
14858       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14859           element == EL_SP_GRAVITY_PORT_RIGHT ||
14860           element == EL_SP_GRAVITY_PORT_UP ||
14861           element == EL_SP_GRAVITY_PORT_DOWN)
14862 #if USE_PLAYER_GRAVITY
14863         player->gravity = !player->gravity;
14864 #else
14865         game.gravity = !game.gravity;
14866 #endif
14867       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14868                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14869                element == EL_SP_GRAVITY_ON_PORT_UP ||
14870                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14871 #if USE_PLAYER_GRAVITY
14872         player->gravity = TRUE;
14873 #else
14874         game.gravity = TRUE;
14875 #endif
14876       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14877                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14878                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14879                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14880 #if USE_PLAYER_GRAVITY
14881         player->gravity = FALSE;
14882 #else
14883         game.gravity = FALSE;
14884 #endif
14885     }
14886
14887     /* automatically move to the next field with double speed */
14888     player->programmed_action = move_direction;
14889
14890     if (player->move_delay_reset_counter == 0)
14891     {
14892       player->move_delay_reset_counter = 2;     /* two double speed steps */
14893
14894       DOUBLE_PLAYER_SPEED(player);
14895     }
14896
14897     PlayLevelSoundAction(x, y, ACTION_PASSING);
14898   }
14899   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14900   {
14901     RemoveField(x, y);
14902
14903     if (mode != DF_SNAP)
14904     {
14905       GfxElement[x][y] = GFX_ELEMENT(element);
14906       player->is_digging = TRUE;
14907     }
14908
14909     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14910
14911     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14912                                         player->index_bit, dig_side);
14913
14914     if (mode == DF_SNAP)
14915     {
14916 #if USE_NEW_SNAP_DELAY
14917       if (level.block_snap_field)
14918         setFieldForSnapping(x, y, element, move_direction);
14919       else
14920         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14921 #else
14922       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14923 #endif
14924
14925       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14926                                           player->index_bit, dig_side);
14927     }
14928   }
14929   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14930   {
14931     RemoveField(x, y);
14932
14933     if (is_player && mode != DF_SNAP)
14934     {
14935       GfxElement[x][y] = element;
14936       player->is_collecting = TRUE;
14937     }
14938
14939     if (element == EL_SPEED_PILL)
14940     {
14941       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14942     }
14943     else if (element == EL_EXTRA_TIME && level.time > 0)
14944     {
14945       TimeLeft += level.extra_time;
14946
14947 #if 1
14948       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14949
14950       DisplayGameControlValues();
14951 #else
14952       DrawGameValue_Time(TimeLeft);
14953 #endif
14954     }
14955     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14956     {
14957       player->shield_normal_time_left += level.shield_normal_time;
14958       if (element == EL_SHIELD_DEADLY)
14959         player->shield_deadly_time_left += level.shield_deadly_time;
14960     }
14961     else if (element == EL_DYNAMITE ||
14962              element == EL_EM_DYNAMITE ||
14963              element == EL_SP_DISK_RED)
14964     {
14965       if (player->inventory_size < MAX_INVENTORY_SIZE)
14966         player->inventory_element[player->inventory_size++] = element;
14967
14968       DrawGameDoorValues();
14969     }
14970     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14971     {
14972       player->dynabomb_count++;
14973       player->dynabombs_left++;
14974     }
14975     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14976     {
14977       player->dynabomb_size++;
14978     }
14979     else if (element == EL_DYNABOMB_INCREASE_POWER)
14980     {
14981       player->dynabomb_xl = TRUE;
14982     }
14983     else if (IS_KEY(element))
14984     {
14985       player->key[KEY_NR(element)] = TRUE;
14986
14987       DrawGameDoorValues();
14988     }
14989     else if (element == EL_DC_KEY_WHITE)
14990     {
14991       player->num_white_keys++;
14992
14993       /* display white keys? */
14994       /* DrawGameDoorValues(); */
14995     }
14996     else if (IS_ENVELOPE(element))
14997     {
14998       player->show_envelope = element;
14999     }
15000     else if (element == EL_EMC_LENSES)
15001     {
15002       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15003
15004       RedrawAllInvisibleElementsForLenses();
15005     }
15006     else if (element == EL_EMC_MAGNIFIER)
15007     {
15008       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15009
15010       RedrawAllInvisibleElementsForMagnifier();
15011     }
15012     else if (IS_DROPPABLE(element) ||
15013              IS_THROWABLE(element))     /* can be collected and dropped */
15014     {
15015       int i;
15016
15017       if (collect_count == 0)
15018         player->inventory_infinite_element = element;
15019       else
15020         for (i = 0; i < collect_count; i++)
15021           if (player->inventory_size < MAX_INVENTORY_SIZE)
15022             player->inventory_element[player->inventory_size++] = element;
15023
15024       DrawGameDoorValues();
15025     }
15026     else if (collect_count > 0)
15027     {
15028       local_player->gems_still_needed -= collect_count;
15029       if (local_player->gems_still_needed < 0)
15030         local_player->gems_still_needed = 0;
15031
15032 #if 1
15033       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15034
15035       DisplayGameControlValues();
15036 #else
15037       DrawGameValue_Emeralds(local_player->gems_still_needed);
15038 #endif
15039     }
15040
15041     RaiseScoreElement(element);
15042     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15043
15044     if (is_player)
15045       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15046                                           player->index_bit, dig_side);
15047
15048     if (mode == DF_SNAP)
15049     {
15050 #if USE_NEW_SNAP_DELAY
15051       if (level.block_snap_field)
15052         setFieldForSnapping(x, y, element, move_direction);
15053       else
15054         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15055 #else
15056       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15057 #endif
15058
15059       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15060                                           player->index_bit, dig_side);
15061     }
15062   }
15063   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15064   {
15065     if (mode == DF_SNAP && element != EL_BD_ROCK)
15066       return MP_NO_ACTION;
15067
15068     if (CAN_FALL(element) && dy)
15069       return MP_NO_ACTION;
15070
15071     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15072         !(element == EL_SPRING && level.use_spring_bug))
15073       return MP_NO_ACTION;
15074
15075     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15076         ((move_direction & MV_VERTICAL &&
15077           ((element_info[element].move_pattern & MV_LEFT &&
15078             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15079            (element_info[element].move_pattern & MV_RIGHT &&
15080             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15081          (move_direction & MV_HORIZONTAL &&
15082           ((element_info[element].move_pattern & MV_UP &&
15083             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15084            (element_info[element].move_pattern & MV_DOWN &&
15085             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15086       return MP_NO_ACTION;
15087
15088     /* do not push elements already moving away faster than player */
15089     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15090         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15091       return MP_NO_ACTION;
15092
15093     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15094     {
15095       if (player->push_delay_value == -1 || !player_was_pushing)
15096         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15097     }
15098     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15099     {
15100       if (player->push_delay_value == -1)
15101         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15102     }
15103     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15104     {
15105       if (!player->is_pushing)
15106         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15107     }
15108
15109     player->is_pushing = TRUE;
15110     player->is_active = TRUE;
15111
15112     if (!(IN_LEV_FIELD(nextx, nexty) &&
15113           (IS_FREE(nextx, nexty) ||
15114            (IS_SB_ELEMENT(element) &&
15115             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15116            (IS_CUSTOM_ELEMENT(element) &&
15117             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15118       return MP_NO_ACTION;
15119
15120     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15121       return MP_NO_ACTION;
15122
15123     if (player->push_delay == -1)       /* new pushing; restart delay */
15124       player->push_delay = 0;
15125
15126     if (player->push_delay < player->push_delay_value &&
15127         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15128         element != EL_SPRING && element != EL_BALLOON)
15129     {
15130       /* make sure that there is no move delay before next try to push */
15131       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15132         player->move_delay = 0;
15133
15134       return MP_NO_ACTION;
15135     }
15136
15137     if (IS_CUSTOM_ELEMENT(element) &&
15138         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15139     {
15140       if (!DigFieldByCE(nextx, nexty, element))
15141         return MP_NO_ACTION;
15142     }
15143
15144     if (IS_SB_ELEMENT(element))
15145     {
15146       if (element == EL_SOKOBAN_FIELD_FULL)
15147       {
15148         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15149         local_player->sokobanfields_still_needed++;
15150       }
15151
15152       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15153       {
15154         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15155         local_player->sokobanfields_still_needed--;
15156       }
15157
15158       Feld[x][y] = EL_SOKOBAN_OBJECT;
15159
15160       if (Back[x][y] == Back[nextx][nexty])
15161         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15162       else if (Back[x][y] != 0)
15163         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15164                                     ACTION_EMPTYING);
15165       else
15166         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15167                                     ACTION_FILLING);
15168
15169       if (local_player->sokobanfields_still_needed == 0 &&
15170           game.emulation == EMU_SOKOBAN)
15171       {
15172         PlayerWins(player);
15173
15174         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15175       }
15176     }
15177     else
15178       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15179
15180     InitMovingField(x, y, move_direction);
15181     GfxAction[x][y] = ACTION_PUSHING;
15182
15183     if (mode == DF_SNAP)
15184       ContinueMoving(x, y);
15185     else
15186       MovPos[x][y] = (dx != 0 ? dx : dy);
15187
15188     Pushed[x][y] = TRUE;
15189     Pushed[nextx][nexty] = TRUE;
15190
15191     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15192       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15193     else
15194       player->push_delay_value = -1;    /* get new value later */
15195
15196     /* check for element change _after_ element has been pushed */
15197     if (game.use_change_when_pushing_bug)
15198     {
15199       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15200                                  player->index_bit, dig_side);
15201       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15202                                           player->index_bit, dig_side);
15203     }
15204   }
15205   else if (IS_SWITCHABLE(element))
15206   {
15207     if (PLAYER_SWITCHING(player, x, y))
15208     {
15209       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15210                                           player->index_bit, dig_side);
15211
15212       return MP_ACTION;
15213     }
15214
15215     player->is_switching = TRUE;
15216     player->switch_x = x;
15217     player->switch_y = y;
15218
15219     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15220
15221     if (element == EL_ROBOT_WHEEL)
15222     {
15223       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15224       ZX = x;
15225       ZY = y;
15226
15227       game.robot_wheel_active = TRUE;
15228
15229       TEST_DrawLevelField(x, y);
15230     }
15231     else if (element == EL_SP_TERMINAL)
15232     {
15233       int xx, yy;
15234
15235       SCAN_PLAYFIELD(xx, yy)
15236       {
15237         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15238           Bang(xx, yy);
15239         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15240           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15241       }
15242     }
15243     else if (IS_BELT_SWITCH(element))
15244     {
15245       ToggleBeltSwitch(x, y);
15246     }
15247     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15248              element == EL_SWITCHGATE_SWITCH_DOWN ||
15249              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15250              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15251     {
15252       ToggleSwitchgateSwitch(x, y);
15253     }
15254     else if (element == EL_LIGHT_SWITCH ||
15255              element == EL_LIGHT_SWITCH_ACTIVE)
15256     {
15257       ToggleLightSwitch(x, y);
15258     }
15259     else if (element == EL_TIMEGATE_SWITCH ||
15260              element == EL_DC_TIMEGATE_SWITCH)
15261     {
15262       ActivateTimegateSwitch(x, y);
15263     }
15264     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15265              element == EL_BALLOON_SWITCH_RIGHT ||
15266              element == EL_BALLOON_SWITCH_UP    ||
15267              element == EL_BALLOON_SWITCH_DOWN  ||
15268              element == EL_BALLOON_SWITCH_NONE  ||
15269              element == EL_BALLOON_SWITCH_ANY)
15270     {
15271       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15272                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15273                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15274                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15275                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15276                              move_direction);
15277     }
15278     else if (element == EL_LAMP)
15279     {
15280       Feld[x][y] = EL_LAMP_ACTIVE;
15281       local_player->lights_still_needed--;
15282
15283       ResetGfxAnimation(x, y);
15284       TEST_DrawLevelField(x, y);
15285     }
15286     else if (element == EL_TIME_ORB_FULL)
15287     {
15288       Feld[x][y] = EL_TIME_ORB_EMPTY;
15289
15290       if (level.time > 0 || level.use_time_orb_bug)
15291       {
15292         TimeLeft += level.time_orb_time;
15293
15294 #if 1
15295         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15296
15297         DisplayGameControlValues();
15298 #else
15299         DrawGameValue_Time(TimeLeft);
15300 #endif
15301       }
15302
15303       ResetGfxAnimation(x, y);
15304       TEST_DrawLevelField(x, y);
15305     }
15306     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15307              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15308     {
15309       int xx, yy;
15310
15311       game.ball_state = !game.ball_state;
15312
15313       SCAN_PLAYFIELD(xx, yy)
15314       {
15315         int e = Feld[xx][yy];
15316
15317         if (game.ball_state)
15318         {
15319           if (e == EL_EMC_MAGIC_BALL)
15320             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15321           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15322             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15323         }
15324         else
15325         {
15326           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15327             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15328           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15329             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15330         }
15331       }
15332     }
15333
15334     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15335                                         player->index_bit, dig_side);
15336
15337     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15338                                         player->index_bit, dig_side);
15339
15340     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15341                                         player->index_bit, dig_side);
15342
15343     return MP_ACTION;
15344   }
15345   else
15346   {
15347     if (!PLAYER_SWITCHING(player, x, y))
15348     {
15349       player->is_switching = TRUE;
15350       player->switch_x = x;
15351       player->switch_y = y;
15352
15353       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15354                                  player->index_bit, dig_side);
15355       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15356                                           player->index_bit, dig_side);
15357
15358       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15359                                  player->index_bit, dig_side);
15360       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15361                                           player->index_bit, dig_side);
15362     }
15363
15364     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15365                                player->index_bit, dig_side);
15366     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367                                         player->index_bit, dig_side);
15368
15369     return MP_NO_ACTION;
15370   }
15371
15372   player->push_delay = -1;
15373
15374   if (is_player)                /* function can also be called by EL_PENGUIN */
15375   {
15376     if (Feld[x][y] != element)          /* really digged/collected something */
15377     {
15378       player->is_collecting = !player->is_digging;
15379       player->is_active = TRUE;
15380     }
15381   }
15382
15383   return MP_MOVING;
15384 }
15385
15386 static boolean DigFieldByCE(int x, int y, int digging_element)
15387 {
15388   int element = Feld[x][y];
15389
15390   if (!IS_FREE(x, y))
15391   {
15392     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15393                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15394                   ACTION_BREAKING);
15395
15396     /* no element can dig solid indestructible elements */
15397     if (IS_INDESTRUCTIBLE(element) &&
15398         !IS_DIGGABLE(element) &&
15399         !IS_COLLECTIBLE(element))
15400       return FALSE;
15401
15402     if (AmoebaNr[x][y] &&
15403         (element == EL_AMOEBA_FULL ||
15404          element == EL_BD_AMOEBA ||
15405          element == EL_AMOEBA_GROWING))
15406     {
15407       AmoebaCnt[AmoebaNr[x][y]]--;
15408       AmoebaCnt2[AmoebaNr[x][y]]--;
15409     }
15410
15411     if (IS_MOVING(x, y))
15412       RemoveMovingField(x, y);
15413     else
15414     {
15415       RemoveField(x, y);
15416       TEST_DrawLevelField(x, y);
15417     }
15418
15419     /* if digged element was about to explode, prevent the explosion */
15420     ExplodeField[x][y] = EX_TYPE_NONE;
15421
15422     PlayLevelSoundAction(x, y, action);
15423   }
15424
15425   Store[x][y] = EL_EMPTY;
15426
15427 #if 1
15428   /* this makes it possible to leave the removed element again */
15429   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15430     Store[x][y] = element;
15431 #else
15432   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15433   {
15434     int move_leave_element = element_info[digging_element].move_leave_element;
15435
15436     /* this makes it possible to leave the removed element again */
15437     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15438                    element : move_leave_element);
15439   }
15440 #endif
15441
15442   return TRUE;
15443 }
15444
15445 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15446 {
15447   int jx = player->jx, jy = player->jy;
15448   int x = jx + dx, y = jy + dy;
15449   int snap_direction = (dx == -1 ? MV_LEFT  :
15450                         dx == +1 ? MV_RIGHT :
15451                         dy == -1 ? MV_UP    :
15452                         dy == +1 ? MV_DOWN  : MV_NONE);
15453   boolean can_continue_snapping = (level.continuous_snapping &&
15454                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15455
15456   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15457     return FALSE;
15458
15459   if (!player->active || !IN_LEV_FIELD(x, y))
15460     return FALSE;
15461
15462   if (dx && dy)
15463     return FALSE;
15464
15465   if (!dx && !dy)
15466   {
15467     if (player->MovPos == 0)
15468       player->is_pushing = FALSE;
15469
15470     player->is_snapping = FALSE;
15471
15472     if (player->MovPos == 0)
15473     {
15474       player->is_moving = FALSE;
15475       player->is_digging = FALSE;
15476       player->is_collecting = FALSE;
15477     }
15478
15479     return FALSE;
15480   }
15481
15482 #if USE_NEW_CONTINUOUS_SNAPPING
15483   /* prevent snapping with already pressed snap key when not allowed */
15484   if (player->is_snapping && !can_continue_snapping)
15485     return FALSE;
15486 #else
15487   if (player->is_snapping)
15488     return FALSE;
15489 #endif
15490
15491   player->MovDir = snap_direction;
15492
15493   if (player->MovPos == 0)
15494   {
15495     player->is_moving = FALSE;
15496     player->is_digging = FALSE;
15497     player->is_collecting = FALSE;
15498   }
15499
15500   player->is_dropping = FALSE;
15501   player->is_dropping_pressed = FALSE;
15502   player->drop_pressed_delay = 0;
15503
15504   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15505     return FALSE;
15506
15507   player->is_snapping = TRUE;
15508   player->is_active = TRUE;
15509
15510   if (player->MovPos == 0)
15511   {
15512     player->is_moving = FALSE;
15513     player->is_digging = FALSE;
15514     player->is_collecting = FALSE;
15515   }
15516
15517   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15518     TEST_DrawLevelField(player->last_jx, player->last_jy);
15519
15520   TEST_DrawLevelField(x, y);
15521
15522   return TRUE;
15523 }
15524
15525 static boolean DropElement(struct PlayerInfo *player)
15526 {
15527   int old_element, new_element;
15528   int dropx = player->jx, dropy = player->jy;
15529   int drop_direction = player->MovDir;
15530   int drop_side = drop_direction;
15531 #if 1
15532   int drop_element = get_next_dropped_element(player);
15533 #else
15534   int drop_element = (player->inventory_size > 0 ?
15535                       player->inventory_element[player->inventory_size - 1] :
15536                       player->inventory_infinite_element != EL_UNDEFINED ?
15537                       player->inventory_infinite_element :
15538                       player->dynabombs_left > 0 ?
15539                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15540                       EL_UNDEFINED);
15541 #endif
15542
15543   player->is_dropping_pressed = TRUE;
15544
15545   /* do not drop an element on top of another element; when holding drop key
15546      pressed without moving, dropped element must move away before the next
15547      element can be dropped (this is especially important if the next element
15548      is dynamite, which can be placed on background for historical reasons) */
15549   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15550     return MP_ACTION;
15551
15552   if (IS_THROWABLE(drop_element))
15553   {
15554     dropx += GET_DX_FROM_DIR(drop_direction);
15555     dropy += GET_DY_FROM_DIR(drop_direction);
15556
15557     if (!IN_LEV_FIELD(dropx, dropy))
15558       return FALSE;
15559   }
15560
15561   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15562   new_element = drop_element;           /* default: no change when dropping */
15563
15564   /* check if player is active, not moving and ready to drop */
15565   if (!player->active || player->MovPos || player->drop_delay > 0)
15566     return FALSE;
15567
15568   /* check if player has anything that can be dropped */
15569   if (new_element == EL_UNDEFINED)
15570     return FALSE;
15571
15572   /* check if drop key was pressed long enough for EM style dynamite */
15573   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15574     return FALSE;
15575
15576   /* check if anything can be dropped at the current position */
15577   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15578     return FALSE;
15579
15580   /* collected custom elements can only be dropped on empty fields */
15581   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15582     return FALSE;
15583
15584   if (old_element != EL_EMPTY)
15585     Back[dropx][dropy] = old_element;   /* store old element on this field */
15586
15587   ResetGfxAnimation(dropx, dropy);
15588   ResetRandomAnimationValue(dropx, dropy);
15589
15590   if (player->inventory_size > 0 ||
15591       player->inventory_infinite_element != EL_UNDEFINED)
15592   {
15593     if (player->inventory_size > 0)
15594     {
15595       player->inventory_size--;
15596
15597       DrawGameDoorValues();
15598
15599       if (new_element == EL_DYNAMITE)
15600         new_element = EL_DYNAMITE_ACTIVE;
15601       else if (new_element == EL_EM_DYNAMITE)
15602         new_element = EL_EM_DYNAMITE_ACTIVE;
15603       else if (new_element == EL_SP_DISK_RED)
15604         new_element = EL_SP_DISK_RED_ACTIVE;
15605     }
15606
15607     Feld[dropx][dropy] = new_element;
15608
15609     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15610       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15611                           el2img(Feld[dropx][dropy]), 0);
15612
15613     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15614
15615     /* needed if previous element just changed to "empty" in the last frame */
15616     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15617
15618     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15619                                player->index_bit, drop_side);
15620     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15621                                         CE_PLAYER_DROPS_X,
15622                                         player->index_bit, drop_side);
15623
15624     TestIfElementTouchesCustomElement(dropx, dropy);
15625   }
15626   else          /* player is dropping a dyna bomb */
15627   {
15628     player->dynabombs_left--;
15629
15630     Feld[dropx][dropy] = new_element;
15631
15632     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15633       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15634                           el2img(Feld[dropx][dropy]), 0);
15635
15636     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15637   }
15638
15639   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15640     InitField_WithBug1(dropx, dropy, FALSE);
15641
15642   new_element = Feld[dropx][dropy];     /* element might have changed */
15643
15644   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15645       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15646   {
15647     int move_direction, nextx, nexty;
15648
15649     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15650       MovDir[dropx][dropy] = drop_direction;
15651
15652     move_direction = MovDir[dropx][dropy];
15653     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15654     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15655
15656     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15657
15658 #if USE_FIX_IMPACT_COLLISION
15659     /* do not cause impact style collision by dropping elements that can fall */
15660     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15661 #else
15662     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15663 #endif
15664   }
15665
15666   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15667   player->is_dropping = TRUE;
15668
15669   player->drop_pressed_delay = 0;
15670   player->is_dropping_pressed = FALSE;
15671
15672   player->drop_x = dropx;
15673   player->drop_y = dropy;
15674
15675   return TRUE;
15676 }
15677
15678 /* ------------------------------------------------------------------------- */
15679 /* game sound playing functions                                              */
15680 /* ------------------------------------------------------------------------- */
15681
15682 static int *loop_sound_frame = NULL;
15683 static int *loop_sound_volume = NULL;
15684
15685 void InitPlayLevelSound()
15686 {
15687   int num_sounds = getSoundListSize();
15688
15689   checked_free(loop_sound_frame);
15690   checked_free(loop_sound_volume);
15691
15692   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15693   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15694 }
15695
15696 static void PlayLevelSound(int x, int y, int nr)
15697 {
15698   int sx = SCREENX(x), sy = SCREENY(y);
15699   int volume, stereo_position;
15700   int max_distance = 8;
15701   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15702
15703   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15704       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15705     return;
15706
15707   if (!IN_LEV_FIELD(x, y) ||
15708       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15709       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15710     return;
15711
15712   volume = SOUND_MAX_VOLUME;
15713
15714   if (!IN_SCR_FIELD(sx, sy))
15715   {
15716     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15717     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15718
15719     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15720   }
15721
15722   stereo_position = (SOUND_MAX_LEFT +
15723                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15724                      (SCR_FIELDX + 2 * max_distance));
15725
15726   if (IS_LOOP_SOUND(nr))
15727   {
15728     /* This assures that quieter loop sounds do not overwrite louder ones,
15729        while restarting sound volume comparison with each new game frame. */
15730
15731     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15732       return;
15733
15734     loop_sound_volume[nr] = volume;
15735     loop_sound_frame[nr] = FrameCounter;
15736   }
15737
15738   PlaySoundExt(nr, volume, stereo_position, type);
15739 }
15740
15741 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15742 {
15743   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15744                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15745                  y < LEVELY(BY1) ? LEVELY(BY1) :
15746                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15747                  sound_action);
15748 }
15749
15750 static void PlayLevelSoundAction(int x, int y, int action)
15751 {
15752   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15753 }
15754
15755 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15756 {
15757   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15758
15759   if (sound_effect != SND_UNDEFINED)
15760     PlayLevelSound(x, y, sound_effect);
15761 }
15762
15763 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15764                                               int action)
15765 {
15766   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15767
15768   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15769     PlayLevelSound(x, y, sound_effect);
15770 }
15771
15772 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15773 {
15774   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15775
15776   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15777     PlayLevelSound(x, y, sound_effect);
15778 }
15779
15780 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15781 {
15782   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15783
15784   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15785     StopSound(sound_effect);
15786 }
15787
15788 static void PlayLevelMusic()
15789 {
15790   if (levelset.music[level_nr] != MUS_UNDEFINED)
15791     PlayMusic(levelset.music[level_nr]);        /* from config file */
15792   else
15793     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15794 }
15795
15796 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15797 {
15798   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15799   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15800   int x = xx - 1 - offset;
15801   int y = yy - 1 - offset;
15802
15803   switch (sample)
15804   {
15805     case SAMPLE_blank:
15806       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15807       break;
15808
15809     case SAMPLE_roll:
15810       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15811       break;
15812
15813     case SAMPLE_stone:
15814       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15815       break;
15816
15817     case SAMPLE_nut:
15818       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15819       break;
15820
15821     case SAMPLE_crack:
15822       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15823       break;
15824
15825     case SAMPLE_bug:
15826       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15827       break;
15828
15829     case SAMPLE_tank:
15830       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15831       break;
15832
15833     case SAMPLE_android_clone:
15834       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15835       break;
15836
15837     case SAMPLE_android_move:
15838       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15839       break;
15840
15841     case SAMPLE_spring:
15842       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15843       break;
15844
15845     case SAMPLE_slurp:
15846       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15847       break;
15848
15849     case SAMPLE_eater:
15850       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15851       break;
15852
15853     case SAMPLE_eater_eat:
15854       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15855       break;
15856
15857     case SAMPLE_alien:
15858       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15859       break;
15860
15861     case SAMPLE_collect:
15862       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15863       break;
15864
15865     case SAMPLE_diamond:
15866       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15867       break;
15868
15869     case SAMPLE_squash:
15870       /* !!! CHECK THIS !!! */
15871 #if 1
15872       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15873 #else
15874       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15875 #endif
15876       break;
15877
15878     case SAMPLE_wonderfall:
15879       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15880       break;
15881
15882     case SAMPLE_drip:
15883       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15884       break;
15885
15886     case SAMPLE_push:
15887       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15888       break;
15889
15890     case SAMPLE_dirt:
15891       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15892       break;
15893
15894     case SAMPLE_acid:
15895       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15896       break;
15897
15898     case SAMPLE_ball:
15899       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15900       break;
15901
15902     case SAMPLE_grow:
15903       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15904       break;
15905
15906     case SAMPLE_wonder:
15907       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15908       break;
15909
15910     case SAMPLE_door:
15911       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15912       break;
15913
15914     case SAMPLE_exit_open:
15915       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15916       break;
15917
15918     case SAMPLE_exit_leave:
15919       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15920       break;
15921
15922     case SAMPLE_dynamite:
15923       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15924       break;
15925
15926     case SAMPLE_tick:
15927       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15928       break;
15929
15930     case SAMPLE_press:
15931       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15932       break;
15933
15934     case SAMPLE_wheel:
15935       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15936       break;
15937
15938     case SAMPLE_boom:
15939       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15940       break;
15941
15942     case SAMPLE_die:
15943       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15944       break;
15945
15946     case SAMPLE_time:
15947       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15948       break;
15949
15950     default:
15951       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15952       break;
15953   }
15954 }
15955
15956 #if 0
15957 void ChangeTime(int value)
15958 {
15959   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15960
15961   *time += value;
15962
15963   /* EMC game engine uses value from time counter of RND game engine */
15964   level.native_em_level->lev->time = *time;
15965
15966   DrawGameValue_Time(*time);
15967 }
15968
15969 void RaiseScore(int value)
15970 {
15971   /* EMC game engine and RND game engine have separate score counters */
15972   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15973                 &level.native_em_level->lev->score : &local_player->score);
15974
15975   *score += value;
15976
15977   DrawGameValue_Score(*score);
15978 }
15979 #endif
15980
15981 void RaiseScore(int value)
15982 {
15983   local_player->score += value;
15984
15985 #if 1
15986   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15987
15988   DisplayGameControlValues();
15989 #else
15990   DrawGameValue_Score(local_player->score);
15991 #endif
15992 }
15993
15994 void RaiseScoreElement(int element)
15995 {
15996   switch (element)
15997   {
15998     case EL_EMERALD:
15999     case EL_BD_DIAMOND:
16000     case EL_EMERALD_YELLOW:
16001     case EL_EMERALD_RED:
16002     case EL_EMERALD_PURPLE:
16003     case EL_SP_INFOTRON:
16004       RaiseScore(level.score[SC_EMERALD]);
16005       break;
16006     case EL_DIAMOND:
16007       RaiseScore(level.score[SC_DIAMOND]);
16008       break;
16009     case EL_CRYSTAL:
16010       RaiseScore(level.score[SC_CRYSTAL]);
16011       break;
16012     case EL_PEARL:
16013       RaiseScore(level.score[SC_PEARL]);
16014       break;
16015     case EL_BUG:
16016     case EL_BD_BUTTERFLY:
16017     case EL_SP_ELECTRON:
16018       RaiseScore(level.score[SC_BUG]);
16019       break;
16020     case EL_SPACESHIP:
16021     case EL_BD_FIREFLY:
16022     case EL_SP_SNIKSNAK:
16023       RaiseScore(level.score[SC_SPACESHIP]);
16024       break;
16025     case EL_YAMYAM:
16026     case EL_DARK_YAMYAM:
16027       RaiseScore(level.score[SC_YAMYAM]);
16028       break;
16029     case EL_ROBOT:
16030       RaiseScore(level.score[SC_ROBOT]);
16031       break;
16032     case EL_PACMAN:
16033       RaiseScore(level.score[SC_PACMAN]);
16034       break;
16035     case EL_NUT:
16036       RaiseScore(level.score[SC_NUT]);
16037       break;
16038     case EL_DYNAMITE:
16039     case EL_EM_DYNAMITE:
16040     case EL_SP_DISK_RED:
16041     case EL_DYNABOMB_INCREASE_NUMBER:
16042     case EL_DYNABOMB_INCREASE_SIZE:
16043     case EL_DYNABOMB_INCREASE_POWER:
16044       RaiseScore(level.score[SC_DYNAMITE]);
16045       break;
16046     case EL_SHIELD_NORMAL:
16047     case EL_SHIELD_DEADLY:
16048       RaiseScore(level.score[SC_SHIELD]);
16049       break;
16050     case EL_EXTRA_TIME:
16051       RaiseScore(level.extra_time_score);
16052       break;
16053     case EL_KEY_1:
16054     case EL_KEY_2:
16055     case EL_KEY_3:
16056     case EL_KEY_4:
16057     case EL_EM_KEY_1:
16058     case EL_EM_KEY_2:
16059     case EL_EM_KEY_3:
16060     case EL_EM_KEY_4:
16061     case EL_EMC_KEY_5:
16062     case EL_EMC_KEY_6:
16063     case EL_EMC_KEY_7:
16064     case EL_EMC_KEY_8:
16065     case EL_DC_KEY_WHITE:
16066       RaiseScore(level.score[SC_KEY]);
16067       break;
16068     default:
16069       RaiseScore(element_info[element].collect_score);
16070       break;
16071   }
16072 }
16073
16074 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16075 {
16076   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16077   {
16078 #if defined(NETWORK_AVALIABLE)
16079     if (options.network)
16080       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16081     else
16082 #endif
16083     {
16084       if (quick_quit)
16085       {
16086 #if 1
16087
16088 #if 1
16089         FadeSkipNextFadeIn();
16090 #else
16091         fading = fading_none;
16092 #endif
16093
16094 #else
16095         OpenDoor(DOOR_CLOSE_1);
16096 #endif
16097
16098         game_status = GAME_MODE_MAIN;
16099
16100 #if 1
16101         DrawAndFadeInMainMenu(REDRAW_FIELD);
16102 #else
16103         DrawMainMenu();
16104 #endif
16105       }
16106       else
16107       {
16108 #if 0
16109         FadeOut(REDRAW_FIELD);
16110 #endif
16111
16112         game_status = GAME_MODE_MAIN;
16113
16114         DrawAndFadeInMainMenu(REDRAW_FIELD);
16115       }
16116     }
16117   }
16118   else          /* continue playing the game */
16119   {
16120     if (tape.playing && tape.deactivate_display)
16121       TapeDeactivateDisplayOff(TRUE);
16122
16123     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16124
16125     if (tape.playing && tape.deactivate_display)
16126       TapeDeactivateDisplayOn();
16127   }
16128 }
16129
16130 void RequestQuitGame(boolean ask_if_really_quit)
16131 {
16132   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16133   boolean skip_request = AllPlayersGone || quick_quit;
16134
16135   RequestQuitGameExt(skip_request, quick_quit,
16136                      "Do you really want to quit the game ?");
16137 }
16138
16139
16140 /* ------------------------------------------------------------------------- */
16141 /* random generator functions                                                */
16142 /* ------------------------------------------------------------------------- */
16143
16144 unsigned int InitEngineRandom_RND(long seed)
16145 {
16146   game.num_random_calls = 0;
16147
16148 #if 0
16149   unsigned int rnd_seed = InitEngineRandom(seed);
16150
16151   printf("::: START RND: %d\n", rnd_seed);
16152
16153   return rnd_seed;
16154 #else
16155
16156   return InitEngineRandom(seed);
16157
16158 #endif
16159
16160 }
16161
16162 unsigned int RND(int max)
16163 {
16164   if (max > 0)
16165   {
16166     game.num_random_calls++;
16167
16168     return GetEngineRandom(max);
16169   }
16170
16171   return 0;
16172 }
16173
16174
16175 /* ------------------------------------------------------------------------- */
16176 /* game engine snapshot handling functions                                   */
16177 /* ------------------------------------------------------------------------- */
16178
16179 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16180
16181 struct EngineSnapshotInfo
16182 {
16183   /* runtime values for custom element collect score */
16184   int collect_score[NUM_CUSTOM_ELEMENTS];
16185
16186   /* runtime values for group element choice position */
16187   int choice_pos[NUM_GROUP_ELEMENTS];
16188
16189   /* runtime values for belt position animations */
16190   int belt_graphic[4 * NUM_BELT_PARTS];
16191   int belt_anim_mode[4 * NUM_BELT_PARTS];
16192 };
16193
16194 struct EngineSnapshotNodeInfo
16195 {
16196   void *buffer_orig;
16197   void *buffer_copy;
16198   int size;
16199 };
16200
16201 static struct EngineSnapshotInfo engine_snapshot_rnd;
16202 static ListNode *engine_snapshot_list = NULL;
16203 static char *snapshot_level_identifier = NULL;
16204 static int snapshot_level_nr = -1;
16205
16206 void FreeEngineSnapshot()
16207 {
16208   while (engine_snapshot_list != NULL)
16209     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16210                        checked_free);
16211
16212   setString(&snapshot_level_identifier, NULL);
16213   snapshot_level_nr = -1;
16214 }
16215
16216 static void SaveEngineSnapshotValues_RND()
16217 {
16218   static int belt_base_active_element[4] =
16219   {
16220     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16221     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16222     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16223     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16224   };
16225   int i, j;
16226
16227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16228   {
16229     int element = EL_CUSTOM_START + i;
16230
16231     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16232   }
16233
16234   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16235   {
16236     int element = EL_GROUP_START + i;
16237
16238     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16239   }
16240
16241   for (i = 0; i < 4; i++)
16242   {
16243     for (j = 0; j < NUM_BELT_PARTS; j++)
16244     {
16245       int element = belt_base_active_element[i] + j;
16246       int graphic = el2img(element);
16247       int anim_mode = graphic_info[graphic].anim_mode;
16248
16249       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16250       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16251     }
16252   }
16253 }
16254
16255 static void LoadEngineSnapshotValues_RND()
16256 {
16257   unsigned long num_random_calls = game.num_random_calls;
16258   int i, j;
16259
16260   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16261   {
16262     int element = EL_CUSTOM_START + i;
16263
16264     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16265   }
16266
16267   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16268   {
16269     int element = EL_GROUP_START + i;
16270
16271     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16272   }
16273
16274   for (i = 0; i < 4; i++)
16275   {
16276     for (j = 0; j < NUM_BELT_PARTS; j++)
16277     {
16278       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16279       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16280
16281       graphic_info[graphic].anim_mode = anim_mode;
16282     }
16283   }
16284
16285   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16286   {
16287     InitRND(tape.random_seed);
16288     for (i = 0; i < num_random_calls; i++)
16289       RND(1);
16290   }
16291
16292   if (game.num_random_calls != num_random_calls)
16293   {
16294     Error(ERR_INFO, "number of random calls out of sync");
16295     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16296     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16297     Error(ERR_EXIT, "this should not happen -- please debug");
16298   }
16299 }
16300
16301 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16302 {
16303   struct EngineSnapshotNodeInfo *bi =
16304     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16305
16306   bi->buffer_orig = buffer;
16307   bi->buffer_copy = checked_malloc(size);
16308   bi->size = size;
16309
16310   memcpy(bi->buffer_copy, buffer, size);
16311
16312   addNodeToList(&engine_snapshot_list, NULL, bi);
16313 }
16314
16315 void SaveEngineSnapshot()
16316 {
16317   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16318
16319   if (level_editor_test_game)   /* do not save snapshots from editor */
16320     return;
16321
16322   /* copy some special values to a structure better suited for the snapshot */
16323
16324   SaveEngineSnapshotValues_RND();
16325   SaveEngineSnapshotValues_EM();
16326
16327   /* save values stored in special snapshot structure */
16328
16329   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16330   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16331
16332   /* save further RND engine values */
16333
16334   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16335   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16336   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16337
16338   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16339   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16340   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16341   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16342
16343   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16344   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16345   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16346   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16347   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16348
16349   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16350   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16351   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16352
16353   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16354
16355   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16356
16357   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16358   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16359
16360   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16361   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16362   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16363   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16364   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16365   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16366   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16367   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16368   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16369   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16370   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16371   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16372   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16373   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16374   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16375   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16376   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16377   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16378
16379   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16380   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16381
16382   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16383   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16385
16386   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16387   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16388
16389   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16390   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16391   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16392   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16393   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16394
16395   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16396   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16397
16398   /* save level identification information */
16399
16400   setString(&snapshot_level_identifier, leveldir_current->identifier);
16401   snapshot_level_nr = level_nr;
16402
16403 #if 0
16404   ListNode *node = engine_snapshot_list;
16405   int num_bytes = 0;
16406
16407   while (node != NULL)
16408   {
16409     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16410
16411     node = node->next;
16412   }
16413
16414   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16415 #endif
16416 }
16417
16418 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16419 {
16420   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16421 }
16422
16423 void LoadEngineSnapshot()
16424 {
16425   ListNode *node = engine_snapshot_list;
16426
16427   if (engine_snapshot_list == NULL)
16428     return;
16429
16430   while (node != NULL)
16431   {
16432     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16433
16434     node = node->next;
16435   }
16436
16437   /* restore special values from snapshot structure */
16438
16439   LoadEngineSnapshotValues_RND();
16440   LoadEngineSnapshotValues_EM();
16441 }
16442
16443 boolean CheckEngineSnapshot()
16444 {
16445   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16446           snapshot_level_nr == level_nr);
16447 }
16448
16449
16450 /* ---------- new game button stuff ---------------------------------------- */
16451
16452 /* graphic position values for game buttons */
16453 #define GAME_BUTTON_XSIZE       30
16454 #define GAME_BUTTON_YSIZE       30
16455 #define GAME_BUTTON_XPOS        5
16456 #define GAME_BUTTON_YPOS        215
16457 #define SOUND_BUTTON_XPOS       5
16458 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16459
16460 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16461 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16462 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16463 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16464 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16465 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16466
16467 static struct
16468 {
16469   int *x, *y;
16470   int gd_x, gd_y;
16471   int gadget_id;
16472   char *infotext;
16473 } gamebutton_info[NUM_GAME_BUTTONS] =
16474 {
16475 #if 1
16476   {
16477     &game.button.stop.x,        &game.button.stop.y,
16478     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16479     GAME_CTRL_ID_STOP,
16480     "stop game"
16481   },
16482   {
16483     &game.button.pause.x,       &game.button.pause.y,
16484     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16485     GAME_CTRL_ID_PAUSE,
16486     "pause game"
16487   },
16488   {
16489     &game.button.play.x,        &game.button.play.y,
16490     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16491     GAME_CTRL_ID_PLAY,
16492     "play game"
16493   },
16494   {
16495     &game.button.sound_music.x, &game.button.sound_music.y,
16496     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16497     SOUND_CTRL_ID_MUSIC,
16498     "background music on/off"
16499   },
16500   {
16501     &game.button.sound_loops.x, &game.button.sound_loops.y,
16502     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16503     SOUND_CTRL_ID_LOOPS,
16504     "sound loops on/off"
16505   },
16506   {
16507     &game.button.sound_simple.x,&game.button.sound_simple.y,
16508     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16509     SOUND_CTRL_ID_SIMPLE,
16510     "normal sounds on/off"
16511   }
16512 #else
16513   {
16514     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16515     GAME_CTRL_ID_STOP,
16516     "stop game"
16517   },
16518   {
16519     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16520     GAME_CTRL_ID_PAUSE,
16521     "pause game"
16522   },
16523   {
16524     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16525     GAME_CTRL_ID_PLAY,
16526     "play game"
16527   },
16528   {
16529     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16530     SOUND_CTRL_ID_MUSIC,
16531     "background music on/off"
16532   },
16533   {
16534     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16535     SOUND_CTRL_ID_LOOPS,
16536     "sound loops on/off"
16537   },
16538   {
16539     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16540     SOUND_CTRL_ID_SIMPLE,
16541     "normal sounds on/off"
16542   }
16543 #endif
16544 };
16545
16546 void CreateGameButtons()
16547 {
16548   int i;
16549
16550   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16551   {
16552     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16553     struct GadgetInfo *gi;
16554     int button_type;
16555     boolean checked;
16556     unsigned long event_mask;
16557     int x, y;
16558     int gd_xoffset, gd_yoffset;
16559     int gd_x1, gd_x2, gd_y1, gd_y2;
16560     int id = i;
16561
16562     x = DX + *gamebutton_info[i].x;
16563     y = DY + *gamebutton_info[i].y;
16564     gd_xoffset = gamebutton_info[i].gd_x;
16565     gd_yoffset = gamebutton_info[i].gd_y;
16566     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16567     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16568
16569     if (id == GAME_CTRL_ID_STOP ||
16570         id == GAME_CTRL_ID_PAUSE ||
16571         id == GAME_CTRL_ID_PLAY)
16572     {
16573       button_type = GD_TYPE_NORMAL_BUTTON;
16574       checked = FALSE;
16575       event_mask = GD_EVENT_RELEASED;
16576       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16577       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16578     }
16579     else
16580     {
16581       button_type = GD_TYPE_CHECK_BUTTON;
16582       checked =
16583         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16584          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16585          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16586       event_mask = GD_EVENT_PRESSED;
16587       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16588       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16589     }
16590
16591     gi = CreateGadget(GDI_CUSTOM_ID, id,
16592                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16593 #if 1
16594                       GDI_X, x,
16595                       GDI_Y, y,
16596 #else
16597                       GDI_X, DX + gd_xoffset,
16598                       GDI_Y, DY + gd_yoffset,
16599 #endif
16600                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16601                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16602                       GDI_TYPE, button_type,
16603                       GDI_STATE, GD_BUTTON_UNPRESSED,
16604                       GDI_CHECKED, checked,
16605                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16606                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16607                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16608                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16609                       GDI_DIRECT_DRAW, FALSE,
16610                       GDI_EVENT_MASK, event_mask,
16611                       GDI_CALLBACK_ACTION, HandleGameButtons,
16612                       GDI_END);
16613
16614     if (gi == NULL)
16615       Error(ERR_EXIT, "cannot create gadget");
16616
16617     game_gadget[id] = gi;
16618   }
16619 }
16620
16621 void FreeGameButtons()
16622 {
16623   int i;
16624
16625   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16626     FreeGadget(game_gadget[i]);
16627 }
16628
16629 static void MapGameButtons()
16630 {
16631   int i;
16632
16633   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16634     MapGadget(game_gadget[i]);
16635 }
16636
16637 void UnmapGameButtons()
16638 {
16639   int i;
16640
16641   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16642     UnmapGadget(game_gadget[i]);
16643 }
16644
16645 void RedrawGameButtons()
16646 {
16647   int i;
16648
16649   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16650     RedrawGadget(game_gadget[i]);
16651 }
16652
16653 static void HandleGameButtons(struct GadgetInfo *gi)
16654 {
16655   int id = gi->custom_id;
16656
16657   if (game_status != GAME_MODE_PLAYING)
16658     return;
16659
16660   switch (id)
16661   {
16662     case GAME_CTRL_ID_STOP:
16663       if (tape.playing)
16664         TapeStop();
16665       else
16666         RequestQuitGame(TRUE);
16667       break;
16668
16669     case GAME_CTRL_ID_PAUSE:
16670       if (options.network)
16671       {
16672 #if defined(NETWORK_AVALIABLE)
16673         if (tape.pausing)
16674           SendToServer_ContinuePlaying();
16675         else
16676           SendToServer_PausePlaying();
16677 #endif
16678       }
16679       else
16680         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16681       break;
16682
16683     case GAME_CTRL_ID_PLAY:
16684       if (tape.pausing)
16685       {
16686 #if defined(NETWORK_AVALIABLE)
16687         if (options.network)
16688           SendToServer_ContinuePlaying();
16689         else
16690 #endif
16691         {
16692           tape.pausing = FALSE;
16693           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16694         }
16695       }
16696       break;
16697
16698     case SOUND_CTRL_ID_MUSIC:
16699       if (setup.sound_music)
16700       { 
16701         setup.sound_music = FALSE;
16702         FadeMusic();
16703       }
16704       else if (audio.music_available)
16705       { 
16706         setup.sound = setup.sound_music = TRUE;
16707
16708         SetAudioMode(setup.sound);
16709
16710         PlayLevelMusic();
16711       }
16712       break;
16713
16714     case SOUND_CTRL_ID_LOOPS:
16715       if (setup.sound_loops)
16716         setup.sound_loops = FALSE;
16717       else if (audio.loops_available)
16718       {
16719         setup.sound = setup.sound_loops = TRUE;
16720         SetAudioMode(setup.sound);
16721       }
16722       break;
16723
16724     case SOUND_CTRL_ID_SIMPLE:
16725       if (setup.sound_simple)
16726         setup.sound_simple = FALSE;
16727       else if (audio.sound_available)
16728       {
16729         setup.sound = setup.sound_simple = TRUE;
16730         SetAudioMode(setup.sound);
16731       }
16732       break;
16733
16734     default:
16735       break;
16736   }
16737 }