a88b3f04ec1a8525e904b45e13418c94580ea9c3
[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_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(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_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(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_FRAME                        35
198 #define GAME_PANEL_SHIELD_NORMAL                36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
200 #define GAME_PANEL_SHIELD_DEADLY                38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
202 #define GAME_PANEL_EXIT                         40
203 #define GAME_PANEL_EMC_MAGIC_BALL               41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
205 #define GAME_PANEL_LIGHT_SWITCH                 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
207 #define GAME_PANEL_TIMEGATE_SWITCH              45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
209 #define GAME_PANEL_SWITCHGATE_SWITCH            47
210 #define GAME_PANEL_EMC_LENSES                   48
211 #define GAME_PANEL_EMC_LENSES_TIME              49
212 #define GAME_PANEL_EMC_MAGNIFIER                50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
214 #define GAME_PANEL_BALLOON_SWITCH               52
215 #define GAME_PANEL_DYNABOMB_NUMBER              53
216 #define GAME_PANEL_DYNABOMB_SIZE                54
217 #define GAME_PANEL_DYNABOMB_POWER               55
218 #define GAME_PANEL_PENGUINS                     56
219 #define GAME_PANEL_SOKOBAN_OBJECTS              57
220 #define GAME_PANEL_SOKOBAN_FIELDS               58
221 #define GAME_PANEL_ROBOT_WHEEL                  59
222 #define GAME_PANEL_CONVEYOR_BELT_1              60
223 #define GAME_PANEL_CONVEYOR_BELT_2              61
224 #define GAME_PANEL_CONVEYOR_BELT_3              62
225 #define GAME_PANEL_CONVEYOR_BELT_4              63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
230 #define GAME_PANEL_MAGIC_WALL                   68
231 #define GAME_PANEL_MAGIC_WALL_TIME              69
232 #define GAME_PANEL_GRAVITY_STATE                70
233 #define GAME_PANEL_GRAPHIC_1                    71
234 #define GAME_PANEL_GRAPHIC_2                    72
235 #define GAME_PANEL_GRAPHIC_3                    73
236 #define GAME_PANEL_GRAPHIC_4                    74
237 #define GAME_PANEL_GRAPHIC_5                    75
238 #define GAME_PANEL_GRAPHIC_6                    76
239 #define GAME_PANEL_GRAPHIC_7                    77
240 #define GAME_PANEL_GRAPHIC_8                    78
241 #define GAME_PANEL_ELEMENT_1                    79
242 #define GAME_PANEL_ELEMENT_2                    80
243 #define GAME_PANEL_ELEMENT_3                    81
244 #define GAME_PANEL_ELEMENT_4                    82
245 #define GAME_PANEL_ELEMENT_5                    83
246 #define GAME_PANEL_ELEMENT_6                    84
247 #define GAME_PANEL_ELEMENT_7                    85
248 #define GAME_PANEL_ELEMENT_8                    86
249 #define GAME_PANEL_ELEMENT_COUNT_1              87
250 #define GAME_PANEL_ELEMENT_COUNT_2              88
251 #define GAME_PANEL_ELEMENT_COUNT_3              89
252 #define GAME_PANEL_ELEMENT_COUNT_4              90
253 #define GAME_PANEL_ELEMENT_COUNT_5              91
254 #define GAME_PANEL_ELEMENT_COUNT_6              92
255 #define GAME_PANEL_ELEMENT_COUNT_7              93
256 #define GAME_PANEL_ELEMENT_COUNT_8              94
257 #define GAME_PANEL_CE_SCORE_1                   95
258 #define GAME_PANEL_CE_SCORE_2                   96
259 #define GAME_PANEL_CE_SCORE_3                   97
260 #define GAME_PANEL_CE_SCORE_4                   98
261 #define GAME_PANEL_CE_SCORE_5                   99
262 #define GAME_PANEL_CE_SCORE_6                   100
263 #define GAME_PANEL_CE_SCORE_7                   101
264 #define GAME_PANEL_CE_SCORE_8                   102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
273 #define GAME_PANEL_PLAYER_NAME                  111
274 #define GAME_PANEL_LEVEL_NAME                   112
275 #define GAME_PANEL_LEVEL_AUTHOR                 113
276
277 #define NUM_GAME_PANEL_CONTROLS                 114
278
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289   int nr;
290
291   struct TextPosInfo *pos;
292   int type;
293
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING      3
884 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION   2
886 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF  -1
890 #define INITIAL_MOVE_DELAY_ON   0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED    32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED   4
896 #define MOVE_DELAY_MAX_SPEED    1
897
898 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN       (1)
907 #define MOVE_STEPSIZE_MAX       (TILEX)
908
909 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911
912 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
915                                  RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
917                                  RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
919                                  RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
921                                     (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
923                                  RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
926                                  RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
928                                  RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
932          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
935         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
936          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
937          (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p)                                         \
940         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
943         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
944          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
945          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
946          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
947          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
948          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
949          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
950          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
951          (e))
952
953 #define CAN_GROW_INTO(e)                                                \
954         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
957                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
968                                         (CAN_MOVE_INTO_ACID(e) &&       \
969                                          Feld[x][y] == EL_ACID) ||      \
970                                         (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
973                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
974                                         (condition) ||                  \
975                                         (CAN_MOVE_INTO_ACID(e) &&       \
976                                          Feld[x][y] == EL_ACID) ||      \
977                                         (DONT_COLLIDE_WITH(e) &&        \
978                                          IS_PLAYER(x, y) &&             \
979                                          !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
991         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1012                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1019         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1022         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1025         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1026                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1031                 (!IS_PLAYER(x, y) &&                                    \
1032                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1035         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP               0
1047 #define GAME_CTRL_ID_PAUSE              1
1048 #define GAME_CTRL_ID_PLAY               2
1049 #define SOUND_CTRL_ID_MUSIC             3
1050 #define SOUND_CTRL_ID_LOOPS             4
1051 #define SOUND_CTRL_ID_SIMPLE            5
1052
1053 #define NUM_GAME_BUTTONS                6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)                             \
1105         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1107         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1109         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1156 {                                                                       \
1157   if (recursion_loop_detected)                                          \
1158     return (rc);                                                        \
1159                                                                         \
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1161   {                                                                     \
1162     recursion_loop_detected = TRUE;                                     \
1163     recursion_loop_element = (e);                                       \
1164   }                                                                     \
1165                                                                         \
1166   recursion_loop_depth++;                                               \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END()                                  \
1170 {                                                                       \
1171   recursion_loop_depth--;                                               \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,                  0, 0 },
1484   { EL_BALLOON,                 0, 0 },
1485
1486   { EL_SOKOBAN_OBJECT,          2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1488   { EL_SATELLITE,               2, 0 },
1489   { EL_SP_DISK_YELLOW,          2, 0 },
1490
1491   { EL_UNDEFINED,               0, 0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,             2 },
1502   { EL_AMOEBA_DROPPING,         2 },
1503   { EL_QUICKSAND_FILLING,       1 },
1504   { EL_QUICKSAND_EMPTYING,      1 },
1505   { EL_QUICKSAND_FAST_FILLING,  2 },
1506   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507   { EL_MAGIC_WALL_FILLING,      2 },
1508   { EL_MAGIC_WALL_EMPTYING,     2 },
1509   { EL_BD_MAGIC_WALL_FILLING,   2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1511   { EL_DC_MAGIC_WALL_FILLING,   2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1513
1514   { EL_UNDEFINED,               0 },
1515 };
1516
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,                 1 },
1525   { EL_BD_DIAMOND,              1 },
1526   { EL_EMERALD_YELLOW,          1 },
1527   { EL_EMERALD_RED,             1 },
1528   { EL_EMERALD_PURPLE,          1 },
1529   { EL_DIAMOND,                 3 },
1530   { EL_SP_INFOTRON,             1 },
1531   { EL_PEARL,                   5 },
1532   { EL_CRYSTAL,                 8 },
1533
1534   { EL_UNDEFINED,               0 },
1535 };
1536
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1555
1556   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1558   { EL_SP_PORT_UP,                                                   MV_DOWN },
1559   { EL_SP_PORT_DOWN,                                         MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1575
1576   { EL_UNDEFINED,                       MV_NONE                              }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1584                                  IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1595                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1596                                      (y) += playfield_scan_delta_y)     \
1597                                 for ((x) = playfield_scan_start_x;      \
1598                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1599                                      (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609         EL_DYNAMITE;
1610 }
1611 #endif
1612
1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632
1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638   InitPlayfieldScanModeVars();
1639 }
1640
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648
1649   return TILEX / move_stepsize;
1650 }
1651
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653                                boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670     player->move_delay_value_next = -1;
1671
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675
1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697
1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710                                     group->choice_mode, 0,
1711                                     group->choice_pos);
1712
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715
1716     group->choice_pos++;
1717
1718     element = group->element_resolved[element_pos];
1719   }
1720
1721   return element;
1722 }
1723
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732         Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734         return;
1735       }
1736       else
1737       {
1738         stored_player[0].initial_element = element;
1739         stored_player[0].use_murphy = TRUE;
1740
1741         if (!level.use_artwork_element[0])
1742           stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753
1754     player->present = TRUE;
1755
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757                                 level.sp_block_last_field :
1758                                 level.block_last_field);
1759
1760     /* ---------- initialize player's last field block delay --------------- */
1761
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779         StorePlayer[jx][jy] = 0;
1780
1781       StorePlayer[x][y] = Feld[x][y];
1782
1783       if (options.debug)
1784       {
1785         printf("Player %d activated.\n", player->element_nr);
1786         printf("[Local player is %d and currently %s.]\n",
1787                local_player->element_nr,
1788                local_player->active ? "active" : "not active");
1789       }
1790     }
1791
1792     Feld[x][y] = EL_EMPTY;
1793
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809
1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899         Feld[x][y] = EL_AMOEBA_GROWING;
1900         Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1949         {
1950           game.belt_dir[belt_nr] = belt_dir;
1951           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952         }
1953         else    /* more than one switch -- set it like the first switch */
1954         {
1955           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956         }
1957       }
1958       break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1962       if (init_game)
1963         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1967       if (init_game)
1968         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981           game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026         if (CAN_MOVE(element))
2027           InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036         Feld[x][y] = GetElementFromGroupElement(element);
2037
2038         InitField(x, y, init_game);
2039       }
2040
2041       break;
2042   }
2043
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061
2062   InitField(x, y, init_game);
2063
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078
2079 #if 1
2080
2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085                           EL_EM_KEY_1 : EL_KEY_1);
2086
2087   return key_base_element + key_nr;
2088 }
2089
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093           player->inventory_element[player->inventory_size - 1] :
2094           player->inventory_infinite_element != EL_UNDEFINED ?
2095           player->inventory_infinite_element :
2096           player->dynabombs_left > 0 ?
2097           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098           EL_UNDEFINED);
2099 }
2100
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112     return (player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             player->inventory_infinite_element != EL_UNDEFINED ?
2115             player->inventory_infinite_element :
2116             player->dynabombs_left >= min_dynabombs_left ?
2117             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118             EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127             player->inventory_infinite_element :
2128             player->dynabombs_left >= min_dynabombs_left ?
2129             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130             player->inventory_size >= min_inventory_size ?
2131             player->inventory_element[inventory_pos] :
2132             EL_UNDEFINED);
2133   }
2134 }
2135
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146
2147   return compare_result;
2148 }
2149
2150 void InitGameControlValues()
2151 {
2152   int i;
2153
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226   int time = (local_player->LevelSolved ?
2227               local_player->LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->time_played :
2232               level.time == 0 ? TimePlayed : TimeLeft);
2233   int score = (local_player->LevelSolved ?
2234                local_player->LevelSolved_CountingScore :
2235                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                level.native_em_level->lev->score :
2237                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                level.native_sp_level->game_sp->score :
2239                local_player->score);
2240   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241               level.native_em_level->lev->required :
2242               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243               level.native_sp_level->game_sp->infotrons_still_needed :
2244               local_player->gems_still_needed);
2245   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                      level.native_em_level->lev->required > 0 :
2247                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249                      local_player->gems_still_needed > 0 ||
2250                      local_player->sokobanfields_still_needed > 0 ||
2251                      local_player->lights_still_needed > 0);
2252
2253   UpdatePlayfieldElementCount();
2254
2255   /* update game panel control values */
2256
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       /* only one player in Supaplex game engine */
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289           level.native_em_level->ply[i]->dynamite;
2290       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292           level.native_sp_level->game_sp->red_disk_count;
2293       else
2294         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295           stored_player[i].inventory_size;
2296
2297       if (stored_player[i].num_white_keys > 0)
2298         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299           EL_DC_KEY_WHITE;
2300
2301       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302         stored_player[i].num_white_keys;
2303     }
2304   }
2305   else
2306   {
2307     int player_nr = game.centered_player_nr;
2308
2309     for (k = 0; k < MAX_NUM_KEYS; k++)
2310     {
2311       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312       {
2313         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315             get_key_element_from_nr(k);
2316       }
2317       else if (stored_player[player_nr].key[k])
2318         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319           get_key_element_from_nr(k);
2320     }
2321
2322     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         level.native_em_level->ply[player_nr]->dynamite;
2325     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327         level.native_sp_level->game_sp->red_disk_count;
2328     else
2329       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330         stored_player[player_nr].inventory_size;
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340   {
2341     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342       get_inventory_element_from_pos(local_player, i);
2343     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344       get_inventory_element_from_pos(local_player, -i - 1);
2345   }
2346
2347   game_panel_controls[GAME_PANEL_SCORE].value = score;
2348   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349
2350   game_panel_controls[GAME_PANEL_TIME].value = time;
2351
2352   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355
2356   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360      EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362     local_player->shield_normal_time_left;
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365      EL_EMPTY);
2366   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367     local_player->shield_deadly_time_left;
2368
2369   game_panel_controls[GAME_PANEL_EXIT].value =
2370     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376      EL_EMC_MAGIC_BALL_SWITCH);
2377
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381     game.light_time_left;
2382
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386     game.timegate_time_left;
2387
2388   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390
2391   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394     game.lenses_time_left;
2395
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399     game.magnify_time_left;
2400
2401   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2403      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2405      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2406      EL_BALLOON_SWITCH_NONE);
2407
2408   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409     local_player->dynabomb_count;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411     local_player->dynabomb_size;
2412   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414
2415   game_panel_controls[GAME_PANEL_PENGUINS].value =
2416     local_player->friends_still_needed;
2417
2418   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419     local_player->sokobanfields_still_needed;
2420   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421     local_player->sokobanfields_still_needed;
2422
2423   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425
2426   for (i = 0; i < NUM_BELTS; i++)
2427   {
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433   }
2434
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438     game.magic_wall_time_left;
2439
2440 #if USE_PLAYER_GRAVITY
2441   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442     local_player->gravity;
2443 #else
2444   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446
2447   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449
2450   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453        game.panel.element[i].id : EL_UNDEFINED);
2454
2455   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458        element_info[game.panel.element_count[i].id].element_count : 0);
2459
2460   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463        element_info[game.panel.ce_score[i].id].collect_score : 0);
2464
2465   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468        element_info[game.panel.ce_score_element[i].id].collect_score :
2469        EL_UNDEFINED);
2470
2471   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474
2475   /* update game panel control frames */
2476
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481     if (gpc->type == TYPE_ELEMENT)
2482     {
2483       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484       {
2485         int last_anim_random_frame = gfx.anim_random_frame;
2486         int element = gpc->value;
2487         int graphic = el2panelimg(element);
2488
2489         if (gpc->value != gpc->last_value)
2490         {
2491           gpc->gfx_frame = 0;
2492           gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494         else
2495         {
2496           gpc->gfx_frame++;
2497
2498           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500             gpc->gfx_random = INIT_GFX_RANDOM();
2501         }
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = gpc->gfx_random;
2505
2506         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507           gpc->gfx_frame = element_info[element].collect_score;
2508
2509         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510                                               gpc->gfx_frame);
2511
2512         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513           gfx.anim_random_frame = last_anim_random_frame;
2514       }
2515     }
2516   }
2517 }
2518
2519 void DisplayGameControlValues()
2520 {
2521   boolean redraw_panel = FALSE;
2522   int i;
2523
2524   for (i = 0; game_panel_controls[i].nr != -1; i++)
2525   {
2526     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527
2528     if (PANEL_DEACTIVATED(gpc->pos))
2529       continue;
2530
2531     if (gpc->value == gpc->last_value &&
2532         gpc->frame == gpc->last_frame)
2533       continue;
2534
2535     redraw_panel = TRUE;
2536   }
2537
2538   if (!redraw_panel)
2539     return;
2540
2541   /* copy default game door content to main double buffer */
2542 #if 1
2543   /* !!! CHECK AGAIN !!! */
2544   SetPanelBackground();
2545   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2547 #else
2548   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2550 #endif
2551
2552   /* redraw game control buttons */
2553 #if 1
2554   RedrawGameButtons();
2555 #else
2556   UnmapGameButtons();
2557   MapGameButtons();
2558 #endif
2559
2560   game_status = GAME_MODE_PSEUDO_PANEL;
2561
2562 #if 1
2563   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2564 #else
2565   for (i = 0; game_panel_controls[i].nr != -1; i++)
2566 #endif
2567   {
2568 #if 1
2569     int nr = game_panel_order[i].nr;
2570     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2571 #else
2572     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2573     int nr = gpc->nr;
2574 #endif
2575     struct TextPosInfo *pos = gpc->pos;
2576     int type = gpc->type;
2577     int value = gpc->value;
2578     int frame = gpc->frame;
2579 #if 0
2580     int last_value = gpc->last_value;
2581     int last_frame = gpc->last_frame;
2582 #endif
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591 #if 0
2592     if (value == last_value && frame == last_frame)
2593       continue;
2594 #endif
2595
2596     gpc->last_value = value;
2597     gpc->last_frame = frame;
2598
2599 #if 0
2600     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2601 #endif
2602
2603     if (type == TYPE_INTEGER)
2604     {
2605       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606           nr == GAME_PANEL_TIME)
2607       {
2608         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2609
2610         if (use_dynamic_size)           /* use dynamic number of digits */
2611         {
2612           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614           int size2 = size1 + 1;
2615           int font1 = pos->font;
2616           int font2 = pos->font_alt;
2617
2618           size = (value < value_change ? size1 : size2);
2619           font = (value < value_change ? font1 : font2);
2620
2621 #if 0
2622           /* clear background if value just changed its size (dynamic digits) */
2623           if ((last_value < value_change) != (value < value_change))
2624           {
2625             int width1 = size1 * getFontWidth(font1);
2626             int width2 = size2 * getFontWidth(font2);
2627             int max_width = MAX(width1, width2);
2628             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2629
2630             pos->width = max_width;
2631
2632             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633                                        max_width, max_height);
2634           }
2635 #endif
2636         }
2637       }
2638
2639 #if 1
2640       /* correct text size if "digits" is zero or less */
2641       if (size <= 0)
2642         size = strlen(int2str(value, size));
2643
2644       /* dynamically correct text alignment */
2645       pos->width = size * getFontWidth(font);
2646 #endif
2647
2648       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649                   int2str(value, size), font, mask_mode);
2650     }
2651     else if (type == TYPE_ELEMENT)
2652     {
2653       int element, graphic;
2654       Bitmap *src_bitmap;
2655       int src_x, src_y;
2656       int width, height;
2657       int dst_x = PANEL_XPOS(pos);
2658       int dst_y = PANEL_YPOS(pos);
2659
2660 #if 1
2661       if (value != EL_UNDEFINED && value != EL_EMPTY)
2662       {
2663         element = value;
2664         graphic = el2panelimg(value);
2665
2666         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2667
2668 #if 1
2669         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2670           size = TILESIZE;
2671 #endif
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2674                               &src_x, &src_y);
2675
2676         width  = graphic_info[graphic].width  * size / TILESIZE;
2677         height = graphic_info[graphic].height * size / TILESIZE;
2678
2679         if (draw_masked)
2680         {
2681           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682                         dst_x - src_x, dst_y - src_y);
2683           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2684                            dst_x, dst_y);
2685         }
2686         else
2687         {
2688           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2689                      dst_x, dst_y);
2690         }
2691       }
2692 #else
2693       if (value == EL_UNDEFINED || value == EL_EMPTY)
2694       {
2695         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696         graphic = el2panelimg(element);
2697
2698         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2701       }
2702       else
2703       {
2704         element = value;
2705         graphic = el2panelimg(value);
2706
2707         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2708       }
2709
2710       width  = graphic_info[graphic].width  * size / TILESIZE;
2711       height = graphic_info[graphic].height * size / TILESIZE;
2712
2713       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2714 #endif
2715     }
2716     else if (type == TYPE_STRING)
2717     {
2718       boolean active = (value != 0);
2719       char *state_normal = "off";
2720       char *state_active = "on";
2721       char *state = (active ? state_active : state_normal);
2722       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2724                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2725                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2726
2727       if (nr == GAME_PANEL_GRAVITY_STATE)
2728       {
2729         int font1 = pos->font;          /* (used for normal state) */
2730         int font2 = pos->font_alt;      /* (used for active state) */
2731 #if 0
2732         int size1 = strlen(state_normal);
2733         int size2 = strlen(state_active);
2734         int width1 = size1 * getFontWidth(font1);
2735         int width2 = size2 * getFontWidth(font2);
2736         int max_width = MAX(width1, width2);
2737         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2738
2739         pos->width = max_width;
2740
2741         /* clear background for values that may have changed its size */
2742         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743                                    max_width, max_height);
2744 #endif
2745
2746         font = (active ? font2 : font1);
2747       }
2748
2749       if (s != NULL)
2750       {
2751         char *s_cut;
2752
2753 #if 1
2754         if (size <= 0)
2755         {
2756           /* don't truncate output if "chars" is zero or less */
2757           size = strlen(s);
2758
2759           /* dynamically correct text alignment */
2760           pos->width = size * getFontWidth(font);
2761         }
2762 #endif
2763
2764         s_cut = getStringCopyN(s, size);
2765
2766         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                     s_cut, font, mask_mode);
2768
2769         free(s_cut);
2770       }
2771     }
2772
2773     redraw_mask |= REDRAW_DOOR_1;
2774   }
2775
2776   game_status = GAME_MODE_PLAYING;
2777 }
2778
2779 void UpdateAndDisplayGameControlValues()
2780 {
2781   if (tape.warp_forward)
2782     return;
2783
2784   UpdateGameControlValues();
2785   DisplayGameControlValues();
2786 }
2787
2788 void DrawGameValue_Emeralds(int value)
2789 {
2790   struct TextPosInfo *pos = &game.panel.gems;
2791 #if 1
2792   int font_nr = pos->font;
2793 #else
2794   int font_nr = FONT_TEXT_2;
2795 #endif
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Dynamite(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.inventory_count;
2814 #if 1
2815   int font_nr = pos->font;
2816 #else
2817   int font_nr = FONT_TEXT_2;
2818 #endif
2819   int font_width = getFontWidth(font_nr);
2820   int chars = pos->size;
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   pos->width = chars * font_width;
2830
2831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2832 }
2833
2834 void DrawGameValue_Score(int value)
2835 {
2836   struct TextPosInfo *pos = &game.panel.score;
2837 #if 1
2838   int font_nr = pos->font;
2839 #else
2840   int font_nr = FONT_TEXT_2;
2841 #endif
2842   int font_width = getFontWidth(font_nr);
2843   int chars = pos->size;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849   if (PANEL_DEACTIVATED(pos))
2850     return;
2851
2852   pos->width = chars * font_width;
2853
2854   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2855 }
2856
2857 void DrawGameValue_Time(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.time;
2860   static int last_value = -1;
2861   int chars1 = 3;
2862   int chars2 = 4;
2863   int chars = pos->size;
2864 #if 1
2865   int font1_nr = pos->font;
2866   int font2_nr = pos->font_alt;
2867 #else
2868   int font1_nr = FONT_TEXT_2;
2869   int font2_nr = FONT_TEXT_1;
2870 #endif
2871   int font_nr = font1_nr;
2872   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2873
2874 #if 1
2875   return;       /* !!! USE NEW STUFF !!! */
2876 #endif
2877
2878   if (PANEL_DEACTIVATED(pos))
2879     return;
2880
2881   if (use_dynamic_chars)                /* use dynamic number of chars */
2882   {
2883     chars   = (value < 1000 ? chars1   : chars2);
2884     font_nr = (value < 1000 ? font1_nr : font2_nr);
2885   }
2886
2887   /* clear background if value just changed its size (dynamic chars only) */
2888   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2889   {
2890     int width1 = chars1 * getFontWidth(font1_nr);
2891     int width2 = chars2 * getFontWidth(font2_nr);
2892     int max_width = MAX(width1, width2);
2893     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2894
2895     pos->width = max_width;
2896
2897     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898                                max_width, max_height);
2899   }
2900
2901   pos->width = chars * getFontWidth(font_nr);
2902
2903   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2904
2905   last_value = value;
2906 }
2907
2908 void DrawGameValue_Level(int value)
2909 {
2910   struct TextPosInfo *pos = &game.panel.level_number;
2911   int chars1 = 2;
2912   int chars2 = 3;
2913   int chars = pos->size;
2914 #if 1
2915   int font1_nr = pos->font;
2916   int font2_nr = pos->font_alt;
2917 #else
2918   int font1_nr = FONT_TEXT_2;
2919   int font2_nr = FONT_TEXT_1;
2920 #endif
2921   int font_nr = font1_nr;
2922   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2923
2924 #if 1
2925   return;       /* !!! USE NEW STUFF !!! */
2926 #endif
2927
2928   if (PANEL_DEACTIVATED(pos))
2929     return;
2930
2931   if (use_dynamic_chars)                /* use dynamic number of chars */
2932   {
2933     chars   = (level_nr < 100 ? chars1   : chars2);
2934     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2935   }
2936
2937   pos->width = chars * getFontWidth(font_nr);
2938
2939   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2940 }
2941
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2943 {
2944 #if 0
2945   struct TextPosInfo *pos = &game.panel.keys;
2946 #endif
2947 #if 0
2948   int base_key_graphic = EL_KEY_1;
2949 #endif
2950   int i;
2951
2952 #if 1
2953   return;       /* !!! USE NEW STUFF !!! */
2954 #endif
2955
2956 #if 0
2957   if (PANEL_DEACTIVATED(pos))
2958     return;
2959 #endif
2960
2961 #if 0
2962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963     base_key_graphic = EL_EM_KEY_1;
2964 #endif
2965
2966 #if 0
2967   pos->width = 4 * MINI_TILEX;
2968 #endif
2969
2970 #if 1
2971   for (i = 0; i < MAX_NUM_KEYS; i++)
2972 #else
2973   /* currently only 4 of 8 possible keys are displayed */
2974   for (i = 0; i < STD_NUM_KEYS; i++)
2975 #endif
2976   {
2977 #if 1
2978     struct TextPosInfo *pos = &game.panel.key[i];
2979 #endif
2980     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981     int src_y = DOOR_GFX_PAGEY1 + 123;
2982 #if 1
2983     int dst_x = PANEL_XPOS(pos);
2984     int dst_y = PANEL_YPOS(pos);
2985 #else
2986     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987     int dst_y = PANEL_YPOS(pos);
2988 #endif
2989
2990 #if 1
2991     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2993                    EL_KEY_1) + i;
2994     int graphic = el2edimg(element);
2995 #endif
2996
2997 #if 1
2998     if (PANEL_DEACTIVATED(pos))
2999       continue;
3000 #endif
3001
3002 #if 0
3003     /* masked blit with tiles from half-size scaled bitmap does not work yet
3004        (no mask bitmap created for these sizes after loading and scaling) --
3005        solution: load without creating mask, scale, then create final mask */
3006
3007     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3009
3010     if (key[i])
3011     {
3012 #if 0
3013       int graphic = el2edimg(base_key_graphic + i);
3014 #endif
3015       Bitmap *src_bitmap;
3016       int src_x, src_y;
3017
3018       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3019
3020       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021                     dst_x - src_x, dst_y - src_y);
3022       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3023                        dst_x, dst_y);
3024     }
3025 #else
3026 #if 1
3027     if (key[i])
3028       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3029     else
3030       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3032 #else
3033     if (key[i])
3034       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3035     else
3036       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3038 #endif
3039 #endif
3040   }
3041 }
3042
3043 #else
3044
3045 void DrawGameValue_Emeralds(int value)
3046 {
3047   int font_nr = FONT_TEXT_2;
3048   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3049
3050   if (PANEL_DEACTIVATED(game.panel.gems))
3051     return;
3052
3053   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3054 }
3055
3056 void DrawGameValue_Dynamite(int value)
3057 {
3058   int font_nr = FONT_TEXT_2;
3059   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060
3061   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3062     return;
3063
3064   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3065 }
3066
3067 void DrawGameValue_Score(int value)
3068 {
3069   int font_nr = FONT_TEXT_2;
3070   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3071
3072   if (PANEL_DEACTIVATED(game.panel.score))
3073     return;
3074
3075   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3076 }
3077
3078 void DrawGameValue_Time(int value)
3079 {
3080   int font1_nr = FONT_TEXT_2;
3081 #if 1
3082   int font2_nr = FONT_TEXT_1;
3083 #else
3084   int font2_nr = FONT_LEVEL_NUMBER;
3085 #endif
3086   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3088
3089   if (PANEL_DEACTIVATED(game.panel.time))
3090     return;
3091
3092   /* clear background if value just changed its size */
3093   if (value == 999 || value == 1000)
3094     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3095
3096   if (value < 1000)
3097     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3098   else
3099     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3100 }
3101
3102 void DrawGameValue_Level(int value)
3103 {
3104   int font1_nr = FONT_TEXT_2;
3105 #if 1
3106   int font2_nr = FONT_TEXT_1;
3107 #else
3108   int font2_nr = FONT_LEVEL_NUMBER;
3109 #endif
3110
3111   if (PANEL_DEACTIVATED(game.panel.level))
3112     return;
3113
3114   if (level_nr < 100)
3115     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3116   else
3117     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3118 }
3119
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3121 {
3122   int base_key_graphic = EL_KEY_1;
3123   int i;
3124
3125   if (PANEL_DEACTIVATED(game.panel.keys))
3126     return;
3127
3128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129     base_key_graphic = EL_EM_KEY_1;
3130
3131   /* currently only 4 of 8 possible keys are displayed */
3132   for (i = 0; i < STD_NUM_KEYS; i++)
3133   {
3134     int x = XX_KEYS + i * MINI_TILEX;
3135     int y = YY_KEYS;
3136
3137     if (key[i])
3138       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3139     else
3140       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3142   }
3143 }
3144
3145 #endif
3146
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3148                        int key_bits)
3149 {
3150   int key[MAX_NUM_KEYS];
3151   int i;
3152
3153   /* prevent EM engine from updating time/score values parallel to GameWon() */
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155       local_player->LevelSolved)
3156     return;
3157
3158   for (i = 0; i < MAX_NUM_KEYS; i++)
3159     key[i] = key_bits & (1 << i);
3160
3161   DrawGameValue_Level(level_nr);
3162
3163   DrawGameValue_Emeralds(emeralds);
3164   DrawGameValue_Dynamite(dynamite);
3165   DrawGameValue_Score(score);
3166   DrawGameValue_Time(time);
3167
3168   DrawGameValue_Keys(key);
3169 }
3170
3171 void UpdateGameDoorValues()
3172 {
3173   UpdateGameControlValues();
3174 }
3175
3176 void DrawGameDoorValues()
3177 {
3178   DisplayGameControlValues();
3179 }
3180
3181 void DrawGameDoorValues_OLD()
3182 {
3183   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184   int dynamite_value = 0;
3185   int score_value = (local_player->LevelSolved ? local_player->score_final :
3186                      local_player->score);
3187   int gems_value = local_player->gems_still_needed;
3188   int key_bits = 0;
3189   int i, j;
3190
3191   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3192   {
3193     DrawGameDoorValues_EM();
3194
3195     return;
3196   }
3197
3198   if (game.centered_player_nr == -1)
3199   {
3200     for (i = 0; i < MAX_PLAYERS; i++)
3201     {
3202       for (j = 0; j < MAX_NUM_KEYS; j++)
3203         if (stored_player[i].key[j])
3204           key_bits |= (1 << j);
3205
3206       dynamite_value += stored_player[i].inventory_size;
3207     }
3208   }
3209   else
3210   {
3211     int player_nr = game.centered_player_nr;
3212
3213     for (i = 0; i < MAX_NUM_KEYS; i++)
3214       if (stored_player[player_nr].key[i])
3215         key_bits |= (1 << i);
3216
3217     dynamite_value = stored_player[player_nr].inventory_size;
3218   }
3219
3220   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3221                     key_bits);
3222 }
3223
3224
3225 /*
3226   =============================================================================
3227   InitGameEngine()
3228   -----------------------------------------------------------------------------
3229   initialize game engine due to level / tape version number
3230   =============================================================================
3231 */
3232
3233 static void InitGameEngine()
3234 {
3235   int i, j, k, l, x, y;
3236
3237   /* set game engine from tape file when re-playing, else from level file */
3238   game.engine_version = (tape.playing ? tape.engine_version :
3239                          level.game_version);
3240
3241   /* ---------------------------------------------------------------------- */
3242   /* set flags for bugs and changes according to active game engine version */
3243   /* ---------------------------------------------------------------------- */
3244
3245   /*
3246     Summary of bugfix/change:
3247     Fixed handling for custom elements that change when pushed by the player.
3248
3249     Fixed/changed in version:
3250     3.1.0
3251
3252     Description:
3253     Before 3.1.0, custom elements that "change when pushing" changed directly
3254     after the player started pushing them (until then handled in "DigField()").
3255     Since 3.1.0, these custom elements are not changed until the "pushing"
3256     move of the element is finished (now handled in "ContinueMoving()").
3257
3258     Affected levels/tapes:
3259     The first condition is generally needed for all levels/tapes before version
3260     3.1.0, which might use the old behaviour before it was changed; known tapes
3261     that are affected are some tapes from the level set "Walpurgis Gardens" by
3262     Jamie Cullen.
3263     The second condition is an exception from the above case and is needed for
3264     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265     above (including some development versions of 3.1.0), but before it was
3266     known that this change would break tapes like the above and was fixed in
3267     3.1.1, so that the changed behaviour was active although the engine version
3268     while recording maybe was before 3.1.0. There is at least one tape that is
3269     affected by this exception, which is the tape for the one-level set "Bug
3270     Machine" by Juergen Bonhagen.
3271   */
3272
3273   game.use_change_when_pushing_bug =
3274     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3275      !(tape.playing &&
3276        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3278
3279   /*
3280     Summary of bugfix/change:
3281     Fixed handling for blocking the field the player leaves when moving.
3282
3283     Fixed/changed in version:
3284     3.1.1
3285
3286     Description:
3287     Before 3.1.1, when "block last field when moving" was enabled, the field
3288     the player is leaving when moving was blocked for the time of the move,
3289     and was directly unblocked afterwards. This resulted in the last field
3290     being blocked for exactly one less than the number of frames of one player
3291     move. Additionally, even when blocking was disabled, the last field was
3292     blocked for exactly one frame.
3293     Since 3.1.1, due to changes in player movement handling, the last field
3294     is not blocked at all when blocking is disabled. When blocking is enabled,
3295     the last field is blocked for exactly the number of frames of one player
3296     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297     last field is blocked for exactly one more than the number of frames of
3298     one player move.
3299
3300     Affected levels/tapes:
3301     (!!! yet to be determined -- probably many !!!)
3302   */
3303
3304   game.use_block_last_field_bug =
3305     (game.engine_version < VERSION_IDENT(3,1,1,0));
3306
3307   /*
3308     Summary of bugfix/change:
3309     Changed behaviour of CE changes with multiple changes per single frame.
3310
3311     Fixed/changed in version:
3312     3.2.0-6
3313
3314     Description:
3315     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316     This resulted in race conditions where CEs seem to behave strange in some
3317     situations (where triggered CE changes were just skipped because there was
3318     already a CE change on that tile in the playfield in that engine frame).
3319     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320     (The number of changes per frame must be limited in any case, because else
3321     it is easily possible to define CE changes that would result in an infinite
3322     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323     should be set large enough so that it would only be reached in cases where
3324     the corresponding CE change conditions run into a loop. Therefore, it seems
3325     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326     maximal number of change pages for custom elements.)
3327
3328     Affected levels/tapes:
3329     Probably many.
3330   */
3331
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333   game.max_num_changes_per_frame = 1;
3334 #else
3335   game.max_num_changes_per_frame =
3336     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3337 #endif
3338
3339   /* ---------------------------------------------------------------------- */
3340
3341   /* default scan direction: scan playfield from top/left to bottom/right */
3342   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3343
3344   /* dynamically adjust element properties according to game engine version */
3345   InitElementPropertiesEngine(game.engine_version);
3346
3347 #if 0
3348   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349   printf("          tape version == %06d [%s] [file: %06d]\n",
3350          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3351          tape.file_version);
3352   printf("       => game.engine_version == %06d\n", game.engine_version);
3353 #endif
3354
3355   /* ---------- initialize player's initial move delay --------------------- */
3356
3357   /* dynamically adjust player properties according to level information */
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359     game.initial_move_delay_value[i] =
3360       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3361
3362   /* dynamically adjust player properties according to game engine version */
3363   for (i = 0; i < MAX_PLAYERS; i++)
3364     game.initial_move_delay[i] =
3365       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366        game.initial_move_delay_value[i] : 0);
3367
3368   /* ---------- initialize player's initial push delay --------------------- */
3369
3370   /* dynamically adjust player properties according to game engine version */
3371   game.initial_push_delay_value =
3372     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3373
3374   /* ---------- initialize changing elements ------------------------------- */
3375
3376   /* initialize changing elements information */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378   {
3379     struct ElementInfo *ei = &element_info[i];
3380
3381     /* this pointer might have been changed in the level editor */
3382     ei->change = &ei->change_page[0];
3383
3384     if (!IS_CUSTOM_ELEMENT(i))
3385     {
3386       ei->change->target_element = EL_EMPTY_SPACE;
3387       ei->change->delay_fixed = 0;
3388       ei->change->delay_random = 0;
3389       ei->change->delay_frames = 1;
3390     }
3391
3392     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3393     {
3394       ei->has_change_event[j] = FALSE;
3395
3396       ei->event_page_nr[j] = 0;
3397       ei->event_page[j] = &ei->change_page[0];
3398     }
3399   }
3400
3401   /* add changing elements from pre-defined list */
3402   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3403   {
3404     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405     struct ElementInfo *ei = &element_info[ch_delay->element];
3406
3407     ei->change->target_element       = ch_delay->target_element;
3408     ei->change->delay_fixed          = ch_delay->change_delay;
3409
3410     ei->change->pre_change_function  = ch_delay->pre_change_function;
3411     ei->change->change_function      = ch_delay->change_function;
3412     ei->change->post_change_function = ch_delay->post_change_function;
3413
3414     ei->change->can_change = TRUE;
3415     ei->change->can_change_or_has_action = TRUE;
3416
3417     ei->has_change_event[CE_DELAY] = TRUE;
3418
3419     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3421   }
3422
3423   /* ---------- initialize internal run-time variables --------------------- */
3424
3425   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3426   {
3427     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3428
3429     for (j = 0; j < ei->num_change_pages; j++)
3430     {
3431       ei->change_page[j].can_change_or_has_action =
3432         (ei->change_page[j].can_change |
3433          ei->change_page[j].has_action);
3434     }
3435   }
3436
3437   /* add change events from custom element configuration */
3438   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3439   {
3440     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3441
3442     for (j = 0; j < ei->num_change_pages; j++)
3443     {
3444       if (!ei->change_page[j].can_change_or_has_action)
3445         continue;
3446
3447       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3448       {
3449         /* only add event page for the first page found with this event */
3450         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3451         {
3452           ei->has_change_event[k] = TRUE;
3453
3454           ei->event_page_nr[k] = j;
3455           ei->event_page[k] = &ei->change_page[j];
3456         }
3457       }
3458     }
3459   }
3460
3461 #if 1
3462   /* ---------- initialize reference elements in change conditions --------- */
3463
3464   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3465   {
3466     int element = EL_CUSTOM_START + i;
3467     struct ElementInfo *ei = &element_info[element];
3468
3469     for (j = 0; j < ei->num_change_pages; j++)
3470     {
3471       int trigger_element = ei->change_page[j].initial_trigger_element;
3472
3473       if (trigger_element >= EL_PREV_CE_8 &&
3474           trigger_element <= EL_NEXT_CE_8)
3475         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3476
3477       ei->change_page[j].trigger_element = trigger_element;
3478     }
3479   }
3480 #endif
3481
3482   /* ---------- initialize run-time trigger player and element ------------- */
3483
3484   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3485   {
3486     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3487
3488     for (j = 0; j < ei->num_change_pages; j++)
3489     {
3490       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494       ei->change_page[j].actual_trigger_ce_value = 0;
3495       ei->change_page[j].actual_trigger_ce_score = 0;
3496     }
3497   }
3498
3499   /* ---------- initialize trigger events ---------------------------------- */
3500
3501   /* initialize trigger events information */
3502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504       trigger_events[i][j] = FALSE;
3505
3506   /* add trigger events from element change event properties */
3507   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3508   {
3509     struct ElementInfo *ei = &element_info[i];
3510
3511     for (j = 0; j < ei->num_change_pages; j++)
3512     {
3513       if (!ei->change_page[j].can_change_or_has_action)
3514         continue;
3515
3516       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3517       {
3518         int trigger_element = ei->change_page[j].trigger_element;
3519
3520         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3521         {
3522           if (ei->change_page[j].has_event[k])
3523           {
3524             if (IS_GROUP_ELEMENT(trigger_element))
3525             {
3526               struct ElementGroupInfo *group =
3527                 element_info[trigger_element].group;
3528
3529               for (l = 0; l < group->num_elements_resolved; l++)
3530                 trigger_events[group->element_resolved[l]][k] = TRUE;
3531             }
3532             else if (trigger_element == EL_ANY_ELEMENT)
3533               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534                 trigger_events[l][k] = TRUE;
3535             else
3536               trigger_events[trigger_element][k] = TRUE;
3537           }
3538         }
3539       }
3540     }
3541   }
3542
3543   /* ---------- initialize push delay -------------------------------------- */
3544
3545   /* initialize push delay values to default */
3546   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3547   {
3548     if (!IS_CUSTOM_ELEMENT(i))
3549     {
3550       /* set default push delay values (corrected since version 3.0.7-1) */
3551       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3552       {
3553         element_info[i].push_delay_fixed = 2;
3554         element_info[i].push_delay_random = 8;
3555       }
3556       else
3557       {
3558         element_info[i].push_delay_fixed = 8;
3559         element_info[i].push_delay_random = 8;
3560       }
3561     }
3562   }
3563
3564   /* set push delay value for certain elements from pre-defined list */
3565   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3566   {
3567     int e = push_delay_list[i].element;
3568
3569     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3570     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3571   }
3572
3573   /* set push delay value for Supaplex elements for newer engine versions */
3574   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3575   {
3576     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577     {
3578       if (IS_SP_ELEMENT(i))
3579       {
3580         /* set SP push delay to just enough to push under a falling zonk */
3581         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3582
3583         element_info[i].push_delay_fixed  = delay;
3584         element_info[i].push_delay_random = 0;
3585       }
3586     }
3587   }
3588
3589   /* ---------- initialize move stepsize ----------------------------------- */
3590
3591   /* initialize move stepsize values to default */
3592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593     if (!IS_CUSTOM_ELEMENT(i))
3594       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3595
3596   /* set move stepsize value for certain elements from pre-defined list */
3597   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3598   {
3599     int e = move_stepsize_list[i].element;
3600
3601     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3602   }
3603
3604   /* ---------- initialize collect score ----------------------------------- */
3605
3606   /* initialize collect score values for custom elements from initial value */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_score = element_info[i].collect_score_initial;
3610
3611   /* ---------- initialize collect count ----------------------------------- */
3612
3613   /* initialize collect count values for non-custom elements */
3614   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615     if (!IS_CUSTOM_ELEMENT(i))
3616       element_info[i].collect_count_initial = 0;
3617
3618   /* add collect count values for all elements from pre-defined list */
3619   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620     element_info[collect_count_list[i].element].collect_count_initial =
3621       collect_count_list[i].count;
3622
3623   /* ---------- initialize access direction -------------------------------- */
3624
3625   /* initialize access direction values to default (access from every side) */
3626   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627     if (!IS_CUSTOM_ELEMENT(i))
3628       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3629
3630   /* set access direction value for certain elements from pre-defined list */
3631   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632     element_info[access_direction_list[i].element].access_direction =
3633       access_direction_list[i].direction;
3634
3635   /* ---------- initialize explosion content ------------------------------- */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637   {
3638     if (IS_CUSTOM_ELEMENT(i))
3639       continue;
3640
3641     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3642     {
3643       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3644
3645       element_info[i].content.e[x][y] =
3646         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648          i == EL_PLAYER_3 ? EL_EMERALD :
3649          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650          i == EL_MOLE ? EL_EMERALD_RED :
3651          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656          i == EL_WALL_EMERALD ? EL_EMERALD :
3657          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662          i == EL_WALL_PEARL ? EL_PEARL :
3663          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3664          EL_EMPTY);
3665     }
3666   }
3667
3668   /* ---------- initialize recursion detection ------------------------------ */
3669   recursion_loop_depth = 0;
3670   recursion_loop_detected = FALSE;
3671   recursion_loop_element = EL_UNDEFINED;
3672
3673   /* ---------- initialize graphics engine ---------------------------------- */
3674   game.scroll_delay_value =
3675     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3677   game.scroll_delay_value =
3678     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3679 }
3680
3681 int get_num_special_action(int element, int action_first, int action_last)
3682 {
3683   int num_special_action = 0;
3684   int i, j;
3685
3686   for (i = action_first; i <= action_last; i++)
3687   {
3688     boolean found = FALSE;
3689
3690     for (j = 0; j < NUM_DIRECTIONS; j++)
3691       if (el_act_dir2img(element, i, j) !=
3692           el_act_dir2img(element, ACTION_DEFAULT, j))
3693         found = TRUE;
3694
3695     if (found)
3696       num_special_action++;
3697     else
3698       break;
3699   }
3700
3701   return num_special_action;
3702 }
3703
3704
3705 /*
3706   =============================================================================
3707   InitGame()
3708   -----------------------------------------------------------------------------
3709   initialize and start new game
3710   =============================================================================
3711 */
3712
3713 void InitGame()
3714 {
3715   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3716   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3717   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3718 #if 0
3719   boolean do_fading = (game_status == GAME_MODE_MAIN);
3720 #endif
3721 #if 1
3722   int initial_move_dir = MV_DOWN;
3723 #else
3724   int initial_move_dir = MV_NONE;
3725 #endif
3726   int i, j, x, y;
3727
3728   game_status = GAME_MODE_PLAYING;
3729
3730 #if 1
3731   /* needed if different viewport properties defined for playing */
3732   ChangeViewportPropertiesIfNeeded();
3733 #endif
3734
3735 #if 1
3736   DrawCompleteVideoDisplay();
3737 #endif
3738
3739   InitGameEngine();
3740   InitGameControlValues();
3741
3742   /* don't play tapes over network */
3743   network_playing = (options.network && !tape.playing);
3744
3745   for (i = 0; i < MAX_PLAYERS; i++)
3746   {
3747     struct PlayerInfo *player = &stored_player[i];
3748
3749     player->index_nr = i;
3750     player->index_bit = (1 << i);
3751     player->element_nr = EL_PLAYER_1 + i;
3752
3753     player->present = FALSE;
3754     player->active = FALSE;
3755     player->mapped = FALSE;
3756
3757     player->killed = FALSE;
3758     player->reanimated = FALSE;
3759
3760     player->action = 0;
3761     player->effective_action = 0;
3762     player->programmed_action = 0;
3763
3764     player->score = 0;
3765     player->score_final = 0;
3766
3767     player->gems_still_needed = level.gems_needed;
3768     player->sokobanfields_still_needed = 0;
3769     player->lights_still_needed = 0;
3770     player->friends_still_needed = 0;
3771
3772     for (j = 0; j < MAX_NUM_KEYS; j++)
3773       player->key[j] = FALSE;
3774
3775     player->num_white_keys = 0;
3776
3777     player->dynabomb_count = 0;
3778     player->dynabomb_size = 1;
3779     player->dynabombs_left = 0;
3780     player->dynabomb_xl = FALSE;
3781
3782     player->MovDir = initial_move_dir;
3783     player->MovPos = 0;
3784     player->GfxPos = 0;
3785     player->GfxDir = initial_move_dir;
3786     player->GfxAction = ACTION_DEFAULT;
3787     player->Frame = 0;
3788     player->StepFrame = 0;
3789
3790     player->initial_element = player->element_nr;
3791     player->artwork_element =
3792       (level.use_artwork_element[i] ? level.artwork_element[i] :
3793        player->element_nr);
3794     player->use_murphy = FALSE;
3795
3796     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3797     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3798
3799     player->gravity = level.initial_player_gravity[i];
3800
3801     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3802
3803     player->actual_frame_counter = 0;
3804
3805     player->step_counter = 0;
3806
3807     player->last_move_dir = initial_move_dir;
3808
3809     player->is_active = FALSE;
3810
3811     player->is_waiting = FALSE;
3812     player->is_moving = FALSE;
3813     player->is_auto_moving = FALSE;
3814     player->is_digging = FALSE;
3815     player->is_snapping = FALSE;
3816     player->is_collecting = FALSE;
3817     player->is_pushing = FALSE;
3818     player->is_switching = FALSE;
3819     player->is_dropping = FALSE;
3820     player->is_dropping_pressed = FALSE;
3821
3822     player->is_bored = FALSE;
3823     player->is_sleeping = FALSE;
3824
3825     player->frame_counter_bored = -1;
3826     player->frame_counter_sleeping = -1;
3827
3828     player->anim_delay_counter = 0;
3829     player->post_delay_counter = 0;
3830
3831     player->dir_waiting = initial_move_dir;
3832     player->action_waiting = ACTION_DEFAULT;
3833     player->last_action_waiting = ACTION_DEFAULT;
3834     player->special_action_bored = ACTION_DEFAULT;
3835     player->special_action_sleeping = ACTION_DEFAULT;
3836
3837     player->switch_x = -1;
3838     player->switch_y = -1;
3839
3840     player->drop_x = -1;
3841     player->drop_y = -1;
3842
3843     player->show_envelope = 0;
3844
3845     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3846
3847     player->push_delay       = -1;      /* initialized when pushing starts */
3848     player->push_delay_value = game.initial_push_delay_value;
3849
3850     player->drop_delay = 0;
3851     player->drop_pressed_delay = 0;
3852
3853     player->last_jx = -1;
3854     player->last_jy = -1;
3855     player->jx = -1;
3856     player->jy = -1;
3857
3858     player->shield_normal_time_left = 0;
3859     player->shield_deadly_time_left = 0;
3860
3861     player->inventory_infinite_element = EL_UNDEFINED;
3862     player->inventory_size = 0;
3863
3864     if (level.use_initial_inventory[i])
3865     {
3866       for (j = 0; j < level.initial_inventory_size[i]; j++)
3867       {
3868         int element = level.initial_inventory_content[i][j];
3869         int collect_count = element_info[element].collect_count_initial;
3870         int k;
3871
3872         if (!IS_CUSTOM_ELEMENT(element))
3873           collect_count = 1;
3874
3875         if (collect_count == 0)
3876           player->inventory_infinite_element = element;
3877         else
3878           for (k = 0; k < collect_count; k++)
3879             if (player->inventory_size < MAX_INVENTORY_SIZE)
3880               player->inventory_element[player->inventory_size++] = element;
3881       }
3882     }
3883
3884     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885     SnapField(player, 0, 0);
3886
3887     player->LevelSolved = FALSE;
3888     player->GameOver = FALSE;
3889
3890     player->LevelSolved_GameWon = FALSE;
3891     player->LevelSolved_GameEnd = FALSE;
3892     player->LevelSolved_PanelOff = FALSE;
3893     player->LevelSolved_SaveTape = FALSE;
3894     player->LevelSolved_SaveScore = FALSE;
3895     player->LevelSolved_CountingTime = 0;
3896     player->LevelSolved_CountingScore = 0;
3897
3898     map_player_action[i] = i;
3899   }
3900
3901   network_player_action_received = FALSE;
3902
3903 #if defined(NETWORK_AVALIABLE)
3904   /* initial null action */
3905   if (network_playing)
3906     SendToServer_MovePlayer(MV_NONE);
3907 #endif
3908
3909   ZX = ZY = -1;
3910   ExitX = ExitY = -1;
3911
3912   FrameCounter = 0;
3913   TimeFrames = 0;
3914   TimePlayed = 0;
3915   TimeLeft = level.time;
3916   TapeTime = 0;
3917
3918   ScreenMovDir = MV_NONE;
3919   ScreenMovPos = 0;
3920   ScreenGfxPos = 0;
3921
3922   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3923
3924   AllPlayersGone = FALSE;
3925
3926   game.yamyam_content_nr = 0;
3927   game.robot_wheel_active = FALSE;
3928   game.magic_wall_active = FALSE;
3929   game.magic_wall_time_left = 0;
3930   game.light_time_left = 0;
3931   game.timegate_time_left = 0;
3932   game.switchgate_pos = 0;
3933   game.wind_direction = level.wind_direction_initial;
3934
3935 #if !USE_PLAYER_GRAVITY
3936   game.gravity = FALSE;
3937   game.explosions_delayed = TRUE;
3938 #endif
3939
3940   game.lenses_time_left = 0;
3941   game.magnify_time_left = 0;
3942
3943   game.ball_state = level.ball_state_initial;
3944   game.ball_content_nr = 0;
3945
3946   game.envelope_active = FALSE;
3947
3948   /* set focus to local player for network games, else to all players */
3949   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950   game.centered_player_nr_next = game.centered_player_nr;
3951   game.set_centered_player = FALSE;
3952
3953   if (network_playing && tape.recording)
3954   {
3955     /* store client dependent player focus when recording network games */
3956     tape.centered_player_nr_next = game.centered_player_nr_next;
3957     tape.set_centered_player = TRUE;
3958   }
3959
3960   for (i = 0; i < NUM_BELTS; i++)
3961   {
3962     game.belt_dir[i] = MV_NONE;
3963     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3964   }
3965
3966   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3968
3969   SCAN_PLAYFIELD(x, y)
3970   {
3971     Feld[x][y] = level.field[x][y];
3972     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973     ChangeDelay[x][y] = 0;
3974     ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976     CustomValue[x][y] = 0;              /* initialized in InitField() */
3977 #endif
3978     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3979     AmoebaNr[x][y] = 0;
3980     WasJustMoving[x][y] = 0;
3981     WasJustFalling[x][y] = 0;
3982     CheckCollision[x][y] = 0;
3983     CheckImpact[x][y] = 0;
3984     Stop[x][y] = FALSE;
3985     Pushed[x][y] = FALSE;
3986
3987     ChangeCount[x][y] = 0;
3988     ChangeEvent[x][y] = -1;
3989
3990     ExplodePhase[x][y] = 0;
3991     ExplodeDelay[x][y] = 0;
3992     ExplodeField[x][y] = EX_TYPE_NONE;
3993
3994     RunnerVisit[x][y] = 0;
3995     PlayerVisit[x][y] = 0;
3996
3997     GfxFrame[x][y] = 0;
3998     GfxRandom[x][y] = INIT_GFX_RANDOM();
3999     GfxElement[x][y] = EL_UNDEFINED;
4000     GfxAction[x][y] = ACTION_DEFAULT;
4001     GfxDir[x][y] = MV_NONE;
4002     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4003   }
4004
4005   SCAN_PLAYFIELD(x, y)
4006   {
4007     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4008       emulate_bd = FALSE;
4009     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4010       emulate_sb = FALSE;
4011     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4012       emulate_sp = FALSE;
4013
4014     InitField(x, y, TRUE);
4015
4016     ResetGfxAnimation(x, y);
4017   }
4018
4019   InitBeltMovement();
4020
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     /* set number of special actions for bored and sleeping animation */
4026     player->num_special_action_bored =
4027       get_num_special_action(player->artwork_element,
4028                              ACTION_BORING_1, ACTION_BORING_LAST);
4029     player->num_special_action_sleeping =
4030       get_num_special_action(player->artwork_element,
4031                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4032   }
4033
4034   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035                     emulate_sb ? EMU_SOKOBAN :
4036                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4037
4038 #if USE_NEW_ALL_SLIPPERY
4039   /* initialize type of slippery elements */
4040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4041   {
4042     if (!IS_CUSTOM_ELEMENT(i))
4043     {
4044       /* default: elements slip down either to the left or right randomly */
4045       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4046
4047       /* SP style elements prefer to slip down on the left side */
4048       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4050
4051       /* BD style elements prefer to slip down on the left side */
4052       if (game.emulation == EMU_BOULDERDASH)
4053         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4054     }
4055   }
4056 #endif
4057
4058   /* initialize explosion and ignition delay */
4059   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4060   {
4061     if (!IS_CUSTOM_ELEMENT(i))
4062     {
4063       int num_phase = 8;
4064       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067       int last_phase = (num_phase + 1) * delay;
4068       int half_phase = (num_phase / 2) * delay;
4069
4070       element_info[i].explosion_delay = last_phase - 1;
4071       element_info[i].ignition_delay = half_phase;
4072
4073       if (i == EL_BLACK_ORB)
4074         element_info[i].ignition_delay = 1;
4075     }
4076
4077 #if 0
4078     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4079       element_info[i].explosion_delay = 1;
4080
4081     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4082       element_info[i].ignition_delay = 1;
4083 #endif
4084   }
4085
4086   /* correct non-moving belts to start moving left */
4087   for (i = 0; i < NUM_BELTS; i++)
4088     if (game.belt_dir[i] == MV_NONE)
4089       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4090
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093   /* choose default local player */
4094   local_player = &stored_player[0];
4095
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097     stored_player[i].connected = FALSE;
4098
4099   local_player->connected = TRUE;
4100   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4101
4102   if (tape.playing)
4103   {
4104     /* try to guess locally connected team mode players (needed for correct
4105        assignment of player figures from level to locally playing players) */
4106
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (tape.player_participates[i])
4109         stored_player[i].connected = TRUE;
4110   }
4111   else if (setup.team_mode && !options.network)
4112   {
4113     /* try to guess locally connected team mode players (needed for correct
4114        assignment of player figures from level to locally playing players) */
4115
4116     for (i = 0; i < MAX_PLAYERS; i++)
4117       if (setup.input[i].use_joystick ||
4118           setup.input[i].key.left != KSYM_UNDEFINED)
4119         stored_player[i].connected = TRUE;
4120   }
4121
4122 #if 0
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124     printf("::: player %d: %s\n", i,
4125            (stored_player[i].connected ? "connected" : "not connected"));
4126
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128     printf("::: player %d: %s\n", i,
4129            (stored_player[i].present ? "present" : "not present"));
4130 #endif
4131
4132   /* check if any connected player was not found in playfield */
4133   for (i = 0; i < MAX_PLAYERS; i++)
4134   {
4135     struct PlayerInfo *player = &stored_player[i];
4136
4137     if (player->connected && !player->present)
4138     {
4139       struct PlayerInfo *field_player = NULL;
4140
4141 #if 0
4142       printf("::: looking for field player for player %d ...\n", i);
4143 #endif
4144
4145       /* assign first free player found that is present in the playfield */
4146
4147       /* first try: look for unmapped playfield player that is not connected */
4148       if (field_player == NULL)
4149         for (j = 0; j < MAX_PLAYERS; j++)
4150           if (stored_player[j].present &&
4151               !stored_player[j].mapped &&
4152               !stored_player[j].connected)
4153             field_player = &stored_player[j];
4154
4155       /* second try: look for *any* unmapped playfield player */
4156       if (field_player == NULL)
4157         for (j = 0; j < MAX_PLAYERS; j++)
4158           if (stored_player[j].present &&
4159               !stored_player[j].mapped)
4160             field_player = &stored_player[j];
4161
4162       if (field_player != NULL)
4163       {
4164         int jx = field_player->jx, jy = field_player->jy;
4165
4166 #if 0
4167         printf("::: found player figure %d\n", field_player->index_nr);
4168 #endif
4169
4170         player->present = FALSE;
4171         player->active = FALSE;
4172
4173         field_player->present = TRUE;
4174         field_player->active = TRUE;
4175
4176         /*
4177         player->initial_element = field_player->initial_element;
4178         player->artwork_element = field_player->artwork_element;
4179
4180         player->block_last_field       = field_player->block_last_field;
4181         player->block_delay_adjustment = field_player->block_delay_adjustment;
4182         */
4183
4184         StorePlayer[jx][jy] = field_player->element_nr;
4185
4186         field_player->jx = field_player->last_jx = jx;
4187         field_player->jy = field_player->last_jy = jy;
4188
4189         if (local_player == player)
4190           local_player = field_player;
4191
4192         map_player_action[field_player->index_nr] = i;
4193
4194         field_player->mapped = TRUE;
4195
4196 #if 0
4197         printf("::: map_player_action[%d] == %d\n",
4198                field_player->index_nr, i);
4199 #endif
4200       }
4201     }
4202
4203     if (player->connected && player->present)
4204       player->mapped = TRUE;
4205   }
4206
4207 #else
4208
4209   /* check if any connected player was not found in playfield */
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211   {
4212     struct PlayerInfo *player = &stored_player[i];
4213
4214     if (player->connected && !player->present)
4215     {
4216       for (j = 0; j < MAX_PLAYERS; j++)
4217       {
4218         struct PlayerInfo *field_player = &stored_player[j];
4219         int jx = field_player->jx, jy = field_player->jy;
4220
4221         /* assign first free player found that is present in the playfield */
4222         if (field_player->present && !field_player->connected)
4223         {
4224           player->present = TRUE;
4225           player->active = TRUE;
4226
4227           field_player->present = FALSE;
4228           field_player->active = FALSE;
4229
4230           player->initial_element = field_player->initial_element;
4231           player->artwork_element = field_player->artwork_element;
4232
4233           player->block_last_field       = field_player->block_last_field;
4234           player->block_delay_adjustment = field_player->block_delay_adjustment;
4235
4236           StorePlayer[jx][jy] = player->element_nr;
4237
4238           player->jx = player->last_jx = jx;
4239           player->jy = player->last_jy = jy;
4240
4241           break;
4242         }
4243       }
4244     }
4245   }
4246 #endif
4247
4248 #if 0
4249   printf("::: local_player->present == %d\n", local_player->present);
4250 #endif
4251
4252   if (tape.playing)
4253   {
4254     /* when playing a tape, eliminate all players who do not participate */
4255
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257     for (i = 0; i < MAX_PLAYERS; i++)
4258     {
4259       if (stored_player[i].active &&
4260           !tape.player_participates[map_player_action[i]])
4261       {
4262         struct PlayerInfo *player = &stored_player[i];
4263         int jx = player->jx, jy = player->jy;
4264
4265         player->active = FALSE;
4266         StorePlayer[jx][jy] = 0;
4267         Feld[jx][jy] = EL_EMPTY;
4268       }
4269     }
4270 #else
4271     for (i = 0; i < MAX_PLAYERS; i++)
4272     {
4273       if (stored_player[i].active &&
4274           !tape.player_participates[i])
4275       {
4276         struct PlayerInfo *player = &stored_player[i];
4277         int jx = player->jx, jy = player->jy;
4278
4279         player->active = FALSE;
4280         StorePlayer[jx][jy] = 0;
4281         Feld[jx][jy] = EL_EMPTY;
4282       }
4283     }
4284 #endif
4285   }
4286   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4287   {
4288     /* when in single player mode, eliminate all but the first active player */
4289
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291     {
4292       if (stored_player[i].active)
4293       {
4294         for (j = i + 1; j < MAX_PLAYERS; j++)
4295         {
4296           if (stored_player[j].active)
4297           {
4298             struct PlayerInfo *player = &stored_player[j];
4299             int jx = player->jx, jy = player->jy;
4300
4301             player->active = FALSE;
4302             player->present = FALSE;
4303
4304             StorePlayer[jx][jy] = 0;
4305             Feld[jx][jy] = EL_EMPTY;
4306           }
4307         }
4308       }
4309     }
4310   }
4311
4312   /* when recording the game, store which players take part in the game */
4313   if (tape.recording)
4314   {
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316     for (i = 0; i < MAX_PLAYERS; i++)
4317       if (stored_player[i].connected)
4318         tape.player_participates[i] = TRUE;
4319 #else
4320     for (i = 0; i < MAX_PLAYERS; i++)
4321       if (stored_player[i].active)
4322         tape.player_participates[i] = TRUE;
4323 #endif
4324   }
4325
4326   if (options.debug)
4327   {
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329     {
4330       struct PlayerInfo *player = &stored_player[i];
4331
4332       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4333              i+1,
4334              player->present,
4335              player->connected,
4336              player->active);
4337       if (local_player == player)
4338         printf("Player  %d is local player.\n", i+1);
4339     }
4340   }
4341
4342   if (BorderElement == EL_EMPTY)
4343   {
4344     SBX_Left = 0;
4345     SBX_Right = lev_fieldx - SCR_FIELDX;
4346     SBY_Upper = 0;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY;
4348   }
4349   else
4350   {
4351     SBX_Left = -1;
4352     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4353     SBY_Upper = -1;
4354     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4355   }
4356
4357   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4358     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4359
4360   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4361     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4362
4363   /* if local player not found, look for custom element that might create
4364      the player (make some assumptions about the right custom element) */
4365   if (!local_player->present)
4366   {
4367     int start_x = 0, start_y = 0;
4368     int found_rating = 0;
4369     int found_element = EL_UNDEFINED;
4370     int player_nr = local_player->index_nr;
4371
4372     SCAN_PLAYFIELD(x, y)
4373     {
4374       int element = Feld[x][y];
4375       int content;
4376       int xx, yy;
4377       boolean is_player;
4378
4379       if (level.use_start_element[player_nr] &&
4380           level.start_element[player_nr] == element &&
4381           found_rating < 4)
4382       {
4383         start_x = x;
4384         start_y = y;
4385
4386         found_rating = 4;
4387         found_element = element;
4388       }
4389
4390       if (!IS_CUSTOM_ELEMENT(element))
4391         continue;
4392
4393       if (CAN_CHANGE(element))
4394       {
4395         for (i = 0; i < element_info[element].num_change_pages; i++)
4396         {
4397           /* check for player created from custom element as single target */
4398           content = element_info[element].change_page[i].target_element;
4399           is_player = ELEM_IS_PLAYER(content);
4400
4401           if (is_player && (found_rating < 3 ||
4402                             (found_rating == 3 && element < found_element)))
4403           {
4404             start_x = x;
4405             start_y = y;
4406
4407             found_rating = 3;
4408             found_element = element;
4409           }
4410         }
4411       }
4412
4413       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4414       {
4415         /* check for player created from custom element as explosion content */
4416         content = element_info[element].content.e[xx][yy];
4417         is_player = ELEM_IS_PLAYER(content);
4418
4419         if (is_player && (found_rating < 2 ||
4420                           (found_rating == 2 && element < found_element)))
4421         {
4422           start_x = x + xx - 1;
4423           start_y = y + yy - 1;
4424
4425           found_rating = 2;
4426           found_element = element;
4427         }
4428
4429         if (!CAN_CHANGE(element))
4430           continue;
4431
4432         for (i = 0; i < element_info[element].num_change_pages; i++)
4433         {
4434           /* check for player created from custom element as extended target */
4435           content =
4436             element_info[element].change_page[i].target_content.e[xx][yy];
4437
4438           is_player = ELEM_IS_PLAYER(content);
4439
4440           if (is_player && (found_rating < 1 ||
4441                             (found_rating == 1 && element < found_element)))
4442           {
4443             start_x = x + xx - 1;
4444             start_y = y + yy - 1;
4445
4446             found_rating = 1;
4447             found_element = element;
4448           }
4449         }
4450       }
4451     }
4452
4453     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4454                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4455                 start_x - MIDPOSX);
4456
4457     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4458                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4459                 start_y - MIDPOSY);
4460   }
4461   else
4462   {
4463     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4464                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4465                 local_player->jx - MIDPOSX);
4466
4467     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4468                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4469                 local_player->jy - MIDPOSY);
4470   }
4471
4472 #if 0
4473   /* do not use PLAYING mask for fading out from main screen */
4474   game_status = GAME_MODE_MAIN;
4475 #endif
4476
4477   StopAnimation();
4478
4479   if (!game.restart_level)
4480     CloseDoor(DOOR_CLOSE_1);
4481
4482 #if 1
4483   if (level_editor_test_game)
4484     FadeSkipNextFadeIn();
4485   else
4486     FadeSetEnterScreen();
4487 #else
4488   if (level_editor_test_game)
4489     fading = fading_none;
4490   else
4491     fading = menu.destination;
4492 #endif
4493
4494 #if 1
4495   FadeOut(REDRAW_FIELD);
4496 #else
4497   if (do_fading)
4498     FadeOut(REDRAW_FIELD);
4499 #endif
4500
4501 #if 0
4502   game_status = GAME_MODE_PLAYING;
4503 #endif
4504
4505   /* !!! FIX THIS (START) !!! */
4506   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4507   {
4508     InitGameEngine_EM();
4509
4510     /* blit playfield from scroll buffer to normal back buffer for fading in */
4511     BlitScreenToBitmap_EM(backbuffer);
4512   }
4513   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4514   {
4515     InitGameEngine_SP();
4516
4517     /* blit playfield from scroll buffer to normal back buffer for fading in */
4518     BlitScreenToBitmap_SP(backbuffer);
4519   }
4520   else
4521   {
4522     DrawLevel();
4523     DrawAllPlayers();
4524
4525     /* after drawing the level, correct some elements */
4526     if (game.timegate_time_left == 0)
4527       CloseAllOpenTimegates();
4528
4529     /* blit playfield from scroll buffer to normal back buffer for fading in */
4530     if (setup.soft_scrolling)
4531       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4532
4533     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4534   }
4535   /* !!! FIX THIS (END) !!! */
4536
4537 #if 1
4538   FadeIn(REDRAW_FIELD);
4539 #else
4540   if (do_fading)
4541     FadeIn(REDRAW_FIELD);
4542
4543   BackToFront();
4544 #endif
4545
4546   if (!game.restart_level)
4547   {
4548     /* copy default game door content to main double buffer */
4549 #if 1
4550 #if 1
4551     /* !!! CHECK AGAIN !!! */
4552     SetPanelBackground();
4553     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4554     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4555 #else
4556     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4557
4558     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4559     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4560     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4561                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4562 #endif
4563 #else
4564     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4565                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4566 #endif
4567   }
4568
4569   SetPanelBackground();
4570   SetDrawBackgroundMask(REDRAW_DOOR_1);
4571
4572 #if 1
4573   UpdateAndDisplayGameControlValues();
4574 #else
4575   UpdateGameDoorValues();
4576   DrawGameDoorValues();
4577 #endif
4578
4579   if (!game.restart_level)
4580   {
4581     UnmapGameButtons();
4582     UnmapTapeButtons();
4583     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4584     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4585     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4586     MapGameButtons();
4587     MapTapeButtons();
4588
4589     /* copy actual game door content to door double buffer for OpenDoor() */
4590     BlitBitmap(drawto, bitmap_db_door,
4591                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4592
4593     OpenDoor(DOOR_OPEN_ALL);
4594
4595     PlaySound(SND_GAME_STARTING);
4596
4597     if (setup.sound_music)
4598       PlayLevelMusic();
4599
4600     KeyboardAutoRepeatOffUnlessAutoplay();
4601
4602     if (options.debug)
4603     {
4604       for (i = 0; i < MAX_PLAYERS; i++)
4605         printf("Player %d %sactive.\n",
4606                i + 1, (stored_player[i].active ? "" : "not "));
4607     }
4608   }
4609
4610 #if 1
4611   UnmapAllGadgets();
4612
4613   MapGameButtons();
4614   MapTapeButtons();
4615 #endif
4616
4617   game.restart_level = FALSE;
4618 }
4619
4620 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4621 {
4622   /* this is used for non-R'n'D game engines to update certain engine values */
4623
4624   /* needed to determine if sounds are played within the visible screen area */
4625   scroll_x = actual_scroll_x;
4626   scroll_y = actual_scroll_y;
4627 }
4628
4629 void InitMovDir(int x, int y)
4630 {
4631   int i, element = Feld[x][y];
4632   static int xy[4][2] =
4633   {
4634     {  0, +1 },
4635     { +1,  0 },
4636     {  0, -1 },
4637     { -1,  0 }
4638   };
4639   static int direction[3][4] =
4640   {
4641     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4642     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4643     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4644   };
4645
4646   switch (element)
4647   {
4648     case EL_BUG_RIGHT:
4649     case EL_BUG_UP:
4650     case EL_BUG_LEFT:
4651     case EL_BUG_DOWN:
4652       Feld[x][y] = EL_BUG;
4653       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4654       break;
4655
4656     case EL_SPACESHIP_RIGHT:
4657     case EL_SPACESHIP_UP:
4658     case EL_SPACESHIP_LEFT:
4659     case EL_SPACESHIP_DOWN:
4660       Feld[x][y] = EL_SPACESHIP;
4661       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4662       break;
4663
4664     case EL_BD_BUTTERFLY_RIGHT:
4665     case EL_BD_BUTTERFLY_UP:
4666     case EL_BD_BUTTERFLY_LEFT:
4667     case EL_BD_BUTTERFLY_DOWN:
4668       Feld[x][y] = EL_BD_BUTTERFLY;
4669       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4670       break;
4671
4672     case EL_BD_FIREFLY_RIGHT:
4673     case EL_BD_FIREFLY_UP:
4674     case EL_BD_FIREFLY_LEFT:
4675     case EL_BD_FIREFLY_DOWN:
4676       Feld[x][y] = EL_BD_FIREFLY;
4677       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4678       break;
4679
4680     case EL_PACMAN_RIGHT:
4681     case EL_PACMAN_UP:
4682     case EL_PACMAN_LEFT:
4683     case EL_PACMAN_DOWN:
4684       Feld[x][y] = EL_PACMAN;
4685       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4686       break;
4687
4688     case EL_YAMYAM_LEFT:
4689     case EL_YAMYAM_RIGHT:
4690     case EL_YAMYAM_UP:
4691     case EL_YAMYAM_DOWN:
4692       Feld[x][y] = EL_YAMYAM;
4693       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4694       break;
4695
4696     case EL_SP_SNIKSNAK:
4697       MovDir[x][y] = MV_UP;
4698       break;
4699
4700     case EL_SP_ELECTRON:
4701       MovDir[x][y] = MV_LEFT;
4702       break;
4703
4704     case EL_MOLE_LEFT:
4705     case EL_MOLE_RIGHT:
4706     case EL_MOLE_UP:
4707     case EL_MOLE_DOWN:
4708       Feld[x][y] = EL_MOLE;
4709       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4710       break;
4711
4712     default:
4713       if (IS_CUSTOM_ELEMENT(element))
4714       {
4715         struct ElementInfo *ei = &element_info[element];
4716         int move_direction_initial = ei->move_direction_initial;
4717         int move_pattern = ei->move_pattern;
4718
4719         if (move_direction_initial == MV_START_PREVIOUS)
4720         {
4721           if (MovDir[x][y] != MV_NONE)
4722             return;
4723
4724           move_direction_initial = MV_START_AUTOMATIC;
4725         }
4726
4727         if (move_direction_initial == MV_START_RANDOM)
4728           MovDir[x][y] = 1 << RND(4);
4729         else if (move_direction_initial & MV_ANY_DIRECTION)
4730           MovDir[x][y] = move_direction_initial;
4731         else if (move_pattern == MV_ALL_DIRECTIONS ||
4732                  move_pattern == MV_TURNING_LEFT ||
4733                  move_pattern == MV_TURNING_RIGHT ||
4734                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4735                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4736                  move_pattern == MV_TURNING_RANDOM)
4737           MovDir[x][y] = 1 << RND(4);
4738         else if (move_pattern == MV_HORIZONTAL)
4739           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4740         else if (move_pattern == MV_VERTICAL)
4741           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4742         else if (move_pattern & MV_ANY_DIRECTION)
4743           MovDir[x][y] = element_info[element].move_pattern;
4744         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4745                  move_pattern == MV_ALONG_RIGHT_SIDE)
4746         {
4747           /* use random direction as default start direction */
4748           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4749             MovDir[x][y] = 1 << RND(4);
4750
4751           for (i = 0; i < NUM_DIRECTIONS; i++)
4752           {
4753             int x1 = x + xy[i][0];
4754             int y1 = y + xy[i][1];
4755
4756             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4757             {
4758               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4759                 MovDir[x][y] = direction[0][i];
4760               else
4761                 MovDir[x][y] = direction[1][i];
4762
4763               break;
4764             }
4765           }
4766         }                
4767       }
4768       else
4769       {
4770         MovDir[x][y] = 1 << RND(4);
4771
4772         if (element != EL_BUG &&
4773             element != EL_SPACESHIP &&
4774             element != EL_BD_BUTTERFLY &&
4775             element != EL_BD_FIREFLY)
4776           break;
4777
4778         for (i = 0; i < NUM_DIRECTIONS; i++)
4779         {
4780           int x1 = x + xy[i][0];
4781           int y1 = y + xy[i][1];
4782
4783           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4784           {
4785             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4786             {
4787               MovDir[x][y] = direction[0][i];
4788               break;
4789             }
4790             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4791                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4792             {
4793               MovDir[x][y] = direction[1][i];
4794               break;
4795             }
4796           }
4797         }
4798       }
4799       break;
4800   }
4801
4802   GfxDir[x][y] = MovDir[x][y];
4803 }
4804
4805 void InitAmoebaNr(int x, int y)
4806 {
4807   int i;
4808   int group_nr = AmoebeNachbarNr(x, y);
4809
4810   if (group_nr == 0)
4811   {
4812     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4813     {
4814       if (AmoebaCnt[i] == 0)
4815       {
4816         group_nr = i;
4817         break;
4818       }
4819     }
4820   }
4821
4822   AmoebaNr[x][y] = group_nr;
4823   AmoebaCnt[group_nr]++;
4824   AmoebaCnt2[group_nr]++;
4825 }
4826
4827 static void PlayerWins(struct PlayerInfo *player)
4828 {
4829   player->LevelSolved = TRUE;
4830   player->GameOver = TRUE;
4831
4832   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4833                          level.native_em_level->lev->score : player->score);
4834
4835   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4836   player->LevelSolved_CountingScore = player->score_final;
4837 }
4838
4839 void GameWon()
4840 {
4841   static int time, time_final;
4842   static int score, score_final;
4843   static int game_over_delay_1 = 0;
4844   static int game_over_delay_2 = 0;
4845   int game_over_delay_value_1 = 50;
4846   int game_over_delay_value_2 = 50;
4847
4848   if (!local_player->LevelSolved_GameWon)
4849   {
4850     int i;
4851
4852     /* do not start end game actions before the player stops moving (to exit) */
4853     if (local_player->MovPos)
4854       return;
4855
4856     local_player->LevelSolved_GameWon = TRUE;
4857     local_player->LevelSolved_SaveTape = tape.recording;
4858     local_player->LevelSolved_SaveScore = !tape.playing;
4859
4860     if (tape.auto_play)         /* tape might already be stopped here */
4861       tape.auto_play_level_solved = TRUE;
4862
4863 #if 1
4864     TapeStop();
4865 #endif
4866
4867     game_over_delay_1 = game_over_delay_value_1;
4868     game_over_delay_2 = game_over_delay_value_2;
4869
4870     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4871     score = score_final = local_player->score_final;
4872
4873     if (TimeLeft > 0)
4874     {
4875       time_final = 0;
4876       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4877     }
4878     else if (level.time == 0 && TimePlayed < 999)
4879     {
4880       time_final = 999;
4881       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4882     }
4883
4884     local_player->score_final = score_final;
4885
4886     if (level_editor_test_game)
4887     {
4888       time = time_final;
4889       score = score_final;
4890
4891 #if 1
4892       local_player->LevelSolved_CountingTime = time;
4893       local_player->LevelSolved_CountingScore = score;
4894
4895       game_panel_controls[GAME_PANEL_TIME].value = time;
4896       game_panel_controls[GAME_PANEL_SCORE].value = score;
4897
4898       DisplayGameControlValues();
4899 #else
4900       DrawGameValue_Time(time);
4901       DrawGameValue_Score(score);
4902 #endif
4903     }
4904
4905     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4906     {
4907       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4908       {
4909         /* close exit door after last player */
4910         if ((AllPlayersGone &&
4911              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4912               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4913               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4914             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4915             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4916         {
4917           int element = Feld[ExitX][ExitY];
4918
4919 #if 0
4920           if (element == EL_EM_EXIT_OPEN ||
4921               element == EL_EM_STEEL_EXIT_OPEN)
4922           {
4923             Bang(ExitX, ExitY);
4924           }
4925           else
4926 #endif
4927           {
4928             Feld[ExitX][ExitY] =
4929               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4930                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4931                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4932                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4933                EL_EM_STEEL_EXIT_CLOSING);
4934
4935             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4936           }
4937         }
4938
4939         /* player disappears */
4940         DrawLevelField(ExitX, ExitY);
4941       }
4942
4943       for (i = 0; i < MAX_PLAYERS; i++)
4944       {
4945         struct PlayerInfo *player = &stored_player[i];
4946
4947         if (player->present)
4948         {
4949           RemovePlayer(player);
4950
4951           /* player disappears */
4952           DrawLevelField(player->jx, player->jy);
4953         }
4954       }
4955     }
4956
4957     PlaySound(SND_GAME_WINNING);
4958   }
4959
4960   if (game_over_delay_1 > 0)
4961   {
4962     game_over_delay_1--;
4963
4964     return;
4965   }
4966
4967   if (time != time_final)
4968   {
4969     int time_to_go = ABS(time_final - time);
4970     int time_count_dir = (time < time_final ? +1 : -1);
4971     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4972
4973     time  += time_count_steps * time_count_dir;
4974     score += time_count_steps * level.score[SC_TIME_BONUS];
4975
4976 #if 1
4977     local_player->LevelSolved_CountingTime = time;
4978     local_player->LevelSolved_CountingScore = score;
4979
4980     game_panel_controls[GAME_PANEL_TIME].value = time;
4981     game_panel_controls[GAME_PANEL_SCORE].value = score;
4982
4983     DisplayGameControlValues();
4984 #else
4985     DrawGameValue_Time(time);
4986     DrawGameValue_Score(score);
4987 #endif
4988
4989     if (time == time_final)
4990       StopSound(SND_GAME_LEVELTIME_BONUS);
4991     else if (setup.sound_loops)
4992       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4993     else
4994       PlaySound(SND_GAME_LEVELTIME_BONUS);
4995
4996     return;
4997   }
4998
4999   local_player->LevelSolved_PanelOff = TRUE;
5000
5001   if (game_over_delay_2 > 0)
5002   {
5003     game_over_delay_2--;
5004
5005     return;
5006   }
5007
5008 #if 1
5009   GameEnd();
5010 #endif
5011 }
5012
5013 void GameEnd()
5014 {
5015   int hi_pos;
5016   boolean raise_level = FALSE;
5017
5018   local_player->LevelSolved_GameEnd = TRUE;
5019
5020   CloseDoor(DOOR_CLOSE_1);
5021
5022   if (local_player->LevelSolved_SaveTape)
5023   {
5024 #if 0
5025     TapeStop();
5026 #endif
5027
5028 #if 1
5029     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5030 #else
5031     SaveTape(tape.level_nr);            /* ask to save tape */
5032 #endif
5033   }
5034
5035   if (level_editor_test_game)
5036   {
5037     game_status = GAME_MODE_MAIN;
5038
5039 #if 1
5040     DrawAndFadeInMainMenu(REDRAW_FIELD);
5041 #else
5042     DrawMainMenu();
5043 #endif
5044
5045     return;
5046   }
5047
5048   if (!local_player->LevelSolved_SaveScore)
5049   {
5050 #if 1
5051     FadeOut(REDRAW_FIELD);
5052 #endif
5053
5054     game_status = GAME_MODE_MAIN;
5055
5056     DrawAndFadeInMainMenu(REDRAW_FIELD);
5057
5058     return;
5059   }
5060
5061   if (level_nr == leveldir_current->handicap_level)
5062   {
5063     leveldir_current->handicap_level++;
5064     SaveLevelSetup_SeriesInfo();
5065   }
5066
5067   if (level_nr < leveldir_current->last_level)
5068     raise_level = TRUE;                 /* advance to next level */
5069
5070   if ((hi_pos = NewHiScore()) >= 0) 
5071   {
5072     game_status = GAME_MODE_SCORES;
5073
5074     DrawHallOfFame(hi_pos);
5075
5076     if (raise_level)
5077     {
5078       level_nr++;
5079       TapeErase();
5080     }
5081   }
5082   else
5083   {
5084 #if 1
5085     FadeOut(REDRAW_FIELD);
5086 #endif
5087
5088     game_status = GAME_MODE_MAIN;
5089
5090     if (raise_level)
5091     {
5092       level_nr++;
5093       TapeErase();
5094     }
5095
5096     DrawAndFadeInMainMenu(REDRAW_FIELD);
5097   }
5098 }
5099
5100 int NewHiScore()
5101 {
5102   int k, l;
5103   int position = -1;
5104
5105   LoadScore(level_nr);
5106
5107   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5108       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5109     return -1;
5110
5111   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5112   {
5113     if (local_player->score_final > highscore[k].Score)
5114     {
5115       /* player has made it to the hall of fame */
5116
5117       if (k < MAX_SCORE_ENTRIES - 1)
5118       {
5119         int m = MAX_SCORE_ENTRIES - 1;
5120
5121 #ifdef ONE_PER_NAME
5122         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5123           if (strEqual(setup.player_name, highscore[l].Name))
5124             m = l;
5125         if (m == k)     /* player's new highscore overwrites his old one */
5126           goto put_into_list;
5127 #endif
5128
5129         for (l = m; l > k; l--)
5130         {
5131           strcpy(highscore[l].Name, highscore[l - 1].Name);
5132           highscore[l].Score = highscore[l - 1].Score;
5133         }
5134       }
5135
5136 #ifdef ONE_PER_NAME
5137       put_into_list:
5138 #endif
5139       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5140       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5141       highscore[k].Score = local_player->score_final; 
5142       position = k;
5143       break;
5144     }
5145
5146 #ifdef ONE_PER_NAME
5147     else if (!strncmp(setup.player_name, highscore[k].Name,
5148                       MAX_PLAYER_NAME_LEN))
5149       break;    /* player already there with a higher score */
5150 #endif
5151
5152   }
5153
5154   if (position >= 0) 
5155     SaveScore(level_nr);
5156
5157   return position;
5158 }
5159
5160 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5161 {
5162   int element = Feld[x][y];
5163   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5164   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5165   int horiz_move = (dx != 0);
5166   int sign = (horiz_move ? dx : dy);
5167   int step = sign * element_info[element].move_stepsize;
5168
5169   /* special values for move stepsize for spring and things on conveyor belt */
5170   if (horiz_move)
5171   {
5172     if (CAN_FALL(element) &&
5173         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5174       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5175     else if (element == EL_SPRING)
5176       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5177   }
5178
5179   return step;
5180 }
5181
5182 inline static int getElementMoveStepsize(int x, int y)
5183 {
5184   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5185 }
5186
5187 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5188 {
5189   if (player->GfxAction != action || player->GfxDir != dir)
5190   {
5191 #if 0
5192     printf("Player frame reset! (%d => %d, %d => %d)\n",
5193            player->GfxAction, action, player->GfxDir, dir);
5194 #endif
5195
5196     player->GfxAction = action;
5197     player->GfxDir = dir;
5198     player->Frame = 0;
5199     player->StepFrame = 0;
5200   }
5201 }
5202
5203 #if USE_GFX_RESET_GFX_ANIMATION
5204 static void ResetGfxFrame(int x, int y, boolean redraw)
5205 {
5206   int element = Feld[x][y];
5207   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5208   int last_gfx_frame = GfxFrame[x][y];
5209
5210   if (graphic_info[graphic].anim_global_sync)
5211     GfxFrame[x][y] = FrameCounter;
5212   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5213     GfxFrame[x][y] = CustomValue[x][y];
5214   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5215     GfxFrame[x][y] = element_info[element].collect_score;
5216   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5217     GfxFrame[x][y] = ChangeDelay[x][y];
5218
5219   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5220     DrawLevelGraphicAnimation(x, y, graphic);
5221 }
5222 #endif
5223
5224 static void ResetGfxAnimation(int x, int y)
5225 {
5226   GfxAction[x][y] = ACTION_DEFAULT;
5227   GfxDir[x][y] = MovDir[x][y];
5228   GfxFrame[x][y] = 0;
5229
5230 #if USE_GFX_RESET_GFX_ANIMATION
5231   ResetGfxFrame(x, y, FALSE);
5232 #endif
5233 }
5234
5235 static void ResetRandomAnimationValue(int x, int y)
5236 {
5237   GfxRandom[x][y] = INIT_GFX_RANDOM();
5238 }
5239
5240 void InitMovingField(int x, int y, int direction)
5241 {
5242   int element = Feld[x][y];
5243   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5244   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5245   int newx = x + dx;
5246   int newy = y + dy;
5247   boolean is_moving_before, is_moving_after;
5248 #if 0
5249   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5250 #endif
5251
5252   /* check if element was/is moving or being moved before/after mode change */
5253 #if 1
5254 #if 1
5255   is_moving_before = (WasJustMoving[x][y] != 0);
5256 #else
5257   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5258   is_moving_before = WasJustMoving[x][y];
5259 #endif
5260 #else
5261   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5262 #endif
5263   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5264
5265   /* reset animation only for moving elements which change direction of moving
5266      or which just started or stopped moving
5267      (else CEs with property "can move" / "not moving" are reset each frame) */
5268 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5269 #if 1
5270   if (is_moving_before != is_moving_after ||
5271       direction != MovDir[x][y])
5272     ResetGfxAnimation(x, y);
5273 #else
5274   if ((is_moving_before || is_moving_after) && !continues_moving)
5275     ResetGfxAnimation(x, y);
5276 #endif
5277 #else
5278   if (!continues_moving)
5279     ResetGfxAnimation(x, y);
5280 #endif
5281
5282   MovDir[x][y] = direction;
5283   GfxDir[x][y] = direction;
5284
5285 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5286   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5287                      direction == MV_DOWN && CAN_FALL(element) ?
5288                      ACTION_FALLING : ACTION_MOVING);
5289 #else
5290   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5291                      ACTION_FALLING : ACTION_MOVING);
5292 #endif
5293
5294   /* this is needed for CEs with property "can move" / "not moving" */
5295
5296   if (is_moving_after)
5297   {
5298     if (Feld[newx][newy] == EL_EMPTY)
5299       Feld[newx][newy] = EL_BLOCKED;
5300
5301     MovDir[newx][newy] = MovDir[x][y];
5302
5303 #if USE_NEW_CUSTOM_VALUE
5304     CustomValue[newx][newy] = CustomValue[x][y];
5305 #endif
5306
5307     GfxFrame[newx][newy] = GfxFrame[x][y];
5308     GfxRandom[newx][newy] = GfxRandom[x][y];
5309     GfxAction[newx][newy] = GfxAction[x][y];
5310     GfxDir[newx][newy] = GfxDir[x][y];
5311   }
5312 }
5313
5314 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5315 {
5316   int direction = MovDir[x][y];
5317   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5318   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5319
5320   *goes_to_x = newx;
5321   *goes_to_y = newy;
5322 }
5323
5324 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5325 {
5326   int oldx = x, oldy = y;
5327   int direction = MovDir[x][y];
5328
5329   if (direction == MV_LEFT)
5330     oldx++;
5331   else if (direction == MV_RIGHT)
5332     oldx--;
5333   else if (direction == MV_UP)
5334     oldy++;
5335   else if (direction == MV_DOWN)
5336     oldy--;
5337
5338   *comes_from_x = oldx;
5339   *comes_from_y = oldy;
5340 }
5341
5342 int MovingOrBlocked2Element(int x, int y)
5343 {
5344   int element = Feld[x][y];
5345
5346   if (element == EL_BLOCKED)
5347   {
5348     int oldx, oldy;
5349
5350     Blocked2Moving(x, y, &oldx, &oldy);
5351     return Feld[oldx][oldy];
5352   }
5353   else
5354     return element;
5355 }
5356
5357 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5358 {
5359   /* like MovingOrBlocked2Element(), but if element is moving
5360      and (x,y) is the field the moving element is just leaving,
5361      return EL_BLOCKED instead of the element value */
5362   int element = Feld[x][y];
5363
5364   if (IS_MOVING(x, y))
5365   {
5366     if (element == EL_BLOCKED)
5367     {
5368       int oldx, oldy;
5369
5370       Blocked2Moving(x, y, &oldx, &oldy);
5371       return Feld[oldx][oldy];
5372     }
5373     else
5374       return EL_BLOCKED;
5375   }
5376   else
5377     return element;
5378 }
5379
5380 static void RemoveField(int x, int y)
5381 {
5382   Feld[x][y] = EL_EMPTY;
5383
5384   MovPos[x][y] = 0;
5385   MovDir[x][y] = 0;
5386   MovDelay[x][y] = 0;
5387
5388 #if USE_NEW_CUSTOM_VALUE
5389   CustomValue[x][y] = 0;
5390 #endif
5391
5392   AmoebaNr[x][y] = 0;
5393   ChangeDelay[x][y] = 0;
5394   ChangePage[x][y] = -1;
5395   Pushed[x][y] = FALSE;
5396
5397 #if 0
5398   ExplodeField[x][y] = EX_TYPE_NONE;
5399 #endif
5400
5401   GfxElement[x][y] = EL_UNDEFINED;
5402   GfxAction[x][y] = ACTION_DEFAULT;
5403   GfxDir[x][y] = MV_NONE;
5404 #if 0
5405   /* !!! this would prevent the removed tile from being redrawn !!! */
5406   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5407 #endif
5408 }
5409
5410 void RemoveMovingField(int x, int y)
5411 {
5412   int oldx = x, oldy = y, newx = x, newy = y;
5413   int element = Feld[x][y];
5414   int next_element = EL_UNDEFINED;
5415
5416   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5417     return;
5418
5419   if (IS_MOVING(x, y))
5420   {
5421     Moving2Blocked(x, y, &newx, &newy);
5422
5423     if (Feld[newx][newy] != EL_BLOCKED)
5424     {
5425       /* element is moving, but target field is not free (blocked), but
5426          already occupied by something different (example: acid pool);
5427          in this case, only remove the moving field, but not the target */
5428
5429       RemoveField(oldx, oldy);
5430
5431       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5432
5433       TEST_DrawLevelField(oldx, oldy);
5434
5435       return;
5436     }
5437   }
5438   else if (element == EL_BLOCKED)
5439   {
5440     Blocked2Moving(x, y, &oldx, &oldy);
5441     if (!IS_MOVING(oldx, oldy))
5442       return;
5443   }
5444
5445   if (element == EL_BLOCKED &&
5446       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5447        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5448        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5449        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5450        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5451        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5452     next_element = get_next_element(Feld[oldx][oldy]);
5453
5454   RemoveField(oldx, oldy);
5455   RemoveField(newx, newy);
5456
5457   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5458
5459   if (next_element != EL_UNDEFINED)
5460     Feld[oldx][oldy] = next_element;
5461
5462   TEST_DrawLevelField(oldx, oldy);
5463   TEST_DrawLevelField(newx, newy);
5464 }
5465
5466 void DrawDynamite(int x, int y)
5467 {
5468   int sx = SCREENX(x), sy = SCREENY(y);
5469   int graphic = el2img(Feld[x][y]);
5470   int frame;
5471
5472   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5473     return;
5474
5475   if (IS_WALKABLE_INSIDE(Back[x][y]))
5476     return;
5477
5478   if (Back[x][y])
5479     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5480   else if (Store[x][y])
5481     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5482
5483   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5484
5485   if (Back[x][y] || Store[x][y])
5486     DrawGraphicThruMask(sx, sy, graphic, frame);
5487   else
5488     DrawGraphic(sx, sy, graphic, frame);
5489 }
5490
5491 void CheckDynamite(int x, int y)
5492 {
5493   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5494   {
5495     MovDelay[x][y]--;
5496
5497     if (MovDelay[x][y] != 0)
5498     {
5499       DrawDynamite(x, y);
5500       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5501
5502       return;
5503     }
5504   }
5505
5506   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5507
5508   Bang(x, y);
5509 }
5510
5511 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5512 {
5513   boolean num_checked_players = 0;
5514   int i;
5515
5516   for (i = 0; i < MAX_PLAYERS; i++)
5517   {
5518     if (stored_player[i].active)
5519     {
5520       int sx = stored_player[i].jx;
5521       int sy = stored_player[i].jy;
5522
5523       if (num_checked_players == 0)
5524       {
5525         *sx1 = *sx2 = sx;
5526         *sy1 = *sy2 = sy;
5527       }
5528       else
5529       {
5530         *sx1 = MIN(*sx1, sx);
5531         *sy1 = MIN(*sy1, sy);
5532         *sx2 = MAX(*sx2, sx);
5533         *sy2 = MAX(*sy2, sy);
5534       }
5535
5536       num_checked_players++;
5537     }
5538   }
5539 }
5540
5541 static boolean checkIfAllPlayersFitToScreen_RND()
5542 {
5543   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5544
5545   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5546
5547   return (sx2 - sx1 < SCR_FIELDX &&
5548           sy2 - sy1 < SCR_FIELDY);
5549 }
5550
5551 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5552 {
5553   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5554
5555   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5556
5557   *sx = (sx1 + sx2) / 2;
5558   *sy = (sy1 + sy2) / 2;
5559 }
5560
5561 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5562                         boolean center_screen, boolean quick_relocation)
5563 {
5564   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5565   boolean no_delay = (tape.warp_forward);
5566   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5567   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5568
5569   if (quick_relocation)
5570   {
5571     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5572     {
5573       if (!level.shifted_relocation || center_screen)
5574       {
5575         /* quick relocation (without scrolling), with centering of screen */
5576
5577         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5578                     x > SBX_Right + MIDPOSX ? SBX_Right :
5579                     x - MIDPOSX);
5580
5581         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583                     y - MIDPOSY);
5584       }
5585       else
5586       {
5587         /* quick relocation (without scrolling), but do not center screen */
5588
5589         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5590                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5591                                old_x - MIDPOSX);
5592
5593         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5595                                old_y - MIDPOSY);
5596
5597         int offset_x = x + (scroll_x - center_scroll_x);
5598         int offset_y = y + (scroll_y - center_scroll_y);
5599
5600         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5601                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5602                     offset_x - MIDPOSX);
5603
5604         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5605                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5606                     offset_y - MIDPOSY);
5607       }
5608     }
5609     else
5610     {
5611 #if 1
5612       if (!level.shifted_relocation || center_screen)
5613       {
5614         /* quick relocation (without scrolling), with centering of screen */
5615
5616         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5617                     x > SBX_Right + MIDPOSX ? SBX_Right :
5618                     x - MIDPOSX);
5619
5620         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622                     y - MIDPOSY);
5623       }
5624       else
5625       {
5626         /* quick relocation (without scrolling), but do not center screen */
5627
5628         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5629                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5630                                old_x - MIDPOSX);
5631
5632         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5633                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5634                                old_y - MIDPOSY);
5635
5636         int offset_x = x + (scroll_x - center_scroll_x);
5637         int offset_y = y + (scroll_y - center_scroll_y);
5638
5639         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5640                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5641                     offset_x - MIDPOSX);
5642
5643         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645                     offset_y - MIDPOSY);
5646       }
5647 #else
5648       /* quick relocation (without scrolling), inside visible screen area */
5649
5650       int offset = game.scroll_delay_value;
5651
5652       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5653           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5654         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5655
5656       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5657           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5658         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5659
5660       /* don't scroll over playfield boundaries */
5661       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5662         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5663
5664       /* don't scroll over playfield boundaries */
5665       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5666         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5667 #endif
5668     }
5669
5670     RedrawPlayfield(TRUE, 0,0,0,0);
5671   }
5672   else
5673   {
5674 #if 1
5675     int scroll_xx, scroll_yy;
5676
5677     if (!level.shifted_relocation || center_screen)
5678     {
5679       /* visible relocation (with scrolling), with centering of screen */
5680
5681       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5682                    x > SBX_Right + MIDPOSX ? SBX_Right :
5683                    x - MIDPOSX);
5684
5685       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5687                    y - MIDPOSY);
5688     }
5689     else
5690     {
5691       /* visible relocation (with scrolling), but do not center screen */
5692
5693       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5694                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5695                              old_x - MIDPOSX);
5696
5697       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5698                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5699                              old_y - MIDPOSY);
5700
5701       int offset_x = x + (scroll_x - center_scroll_x);
5702       int offset_y = y + (scroll_y - center_scroll_y);
5703
5704       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5705                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5706                    offset_x - MIDPOSX);
5707
5708       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5709                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5710                    offset_y - MIDPOSY);
5711     }
5712
5713 #else
5714
5715     /* visible relocation (with scrolling), with centering of screen */
5716
5717     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5718                      x > SBX_Right + MIDPOSX ? SBX_Right :
5719                      x - MIDPOSX);
5720
5721     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5722                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5723                      y - MIDPOSY);
5724 #endif
5725
5726     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5727
5728     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5729     {
5730       int dx = 0, dy = 0;
5731       int fx = FX, fy = FY;
5732
5733       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5734       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5735
5736       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5737         break;
5738
5739       scroll_x -= dx;
5740       scroll_y -= dy;
5741
5742       fx += dx * TILEX / 2;
5743       fy += dy * TILEY / 2;
5744
5745       ScrollLevel(dx, dy);
5746       DrawAllPlayers();
5747
5748       /* scroll in two steps of half tile size to make things smoother */
5749       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5750       FlushDisplay();
5751       Delay(wait_delay_value);
5752
5753       /* scroll second step to align at full tile size */
5754       BackToFront();
5755       Delay(wait_delay_value);
5756     }
5757
5758     DrawAllPlayers();
5759     BackToFront();
5760     Delay(wait_delay_value);
5761   }
5762 }
5763
5764 void RelocatePlayer(int jx, int jy, int el_player_raw)
5765 {
5766   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5767   int player_nr = GET_PLAYER_NR(el_player);
5768   struct PlayerInfo *player = &stored_player[player_nr];
5769   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5770   boolean no_delay = (tape.warp_forward);
5771   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5772   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5773   int old_jx = player->jx;
5774   int old_jy = player->jy;
5775   int old_element = Feld[old_jx][old_jy];
5776   int element = Feld[jx][jy];
5777   boolean player_relocated = (old_jx != jx || old_jy != jy);
5778
5779   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5780   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5781   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5782   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5783   int leave_side_horiz = move_dir_horiz;
5784   int leave_side_vert  = move_dir_vert;
5785   int enter_side = enter_side_horiz | enter_side_vert;
5786   int leave_side = leave_side_horiz | leave_side_vert;
5787
5788   if (player->GameOver)         /* do not reanimate dead player */
5789     return;
5790
5791   if (!player_relocated)        /* no need to relocate the player */
5792     return;
5793
5794   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5795   {
5796     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5797     DrawLevelField(jx, jy);
5798   }
5799
5800   if (player->present)
5801   {
5802     while (player->MovPos)
5803     {
5804       ScrollPlayer(player, SCROLL_GO_ON);
5805       ScrollScreen(NULL, SCROLL_GO_ON);
5806
5807       AdvanceFrameAndPlayerCounters(player->index_nr);
5808
5809       DrawPlayer(player);
5810
5811       BackToFront();
5812       Delay(wait_delay_value);
5813     }
5814
5815     DrawPlayer(player);         /* needed here only to cleanup last field */
5816     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5817
5818     player->is_moving = FALSE;
5819   }
5820
5821   if (IS_CUSTOM_ELEMENT(old_element))
5822     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5823                                CE_LEFT_BY_PLAYER,
5824                                player->index_bit, leave_side);
5825
5826   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5827                                       CE_PLAYER_LEAVES_X,
5828                                       player->index_bit, leave_side);
5829
5830   Feld[jx][jy] = el_player;
5831   InitPlayerField(jx, jy, el_player, TRUE);
5832
5833   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5834      possible that the relocation target field did not contain a player element,
5835      but a walkable element, to which the new player was relocated -- in this
5836      case, restore that (already initialized!) element on the player field */
5837   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5838   {
5839     Feld[jx][jy] = element;     /* restore previously existing element */
5840 #if 0
5841     /* !!! do not initialize already initialized element a second time !!! */
5842     /* (this causes at least problems with "element creation" CE trigger for
5843        already existing elements, and existing Sokoban fields counted twice) */
5844     InitField(jx, jy, FALSE);
5845 #endif
5846   }
5847
5848   /* only visually relocate centered player */
5849   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5850                      FALSE, level.instant_relocation);
5851
5852   TestIfPlayerTouchesBadThing(jx, jy);
5853   TestIfPlayerTouchesCustomElement(jx, jy);
5854
5855   if (IS_CUSTOM_ELEMENT(element))
5856     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5857                                player->index_bit, enter_side);
5858
5859   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5860                                       player->index_bit, enter_side);
5861
5862 #if 1
5863   if (player->is_switching)
5864   {
5865     /* ensure that relocation while still switching an element does not cause
5866        a new element to be treated as also switched directly after relocation
5867        (this is important for teleporter switches that teleport the player to
5868        a place where another teleporter switch is in the same direction, which
5869        would then incorrectly be treated as immediately switched before the
5870        direction key that caused the switch was released) */
5871
5872     player->switch_x += jx - old_jx;
5873     player->switch_y += jy - old_jy;
5874   }
5875 #endif
5876 }
5877
5878 void Explode(int ex, int ey, int phase, int mode)
5879 {
5880   int x, y;
5881   int last_phase;
5882   int border_element;
5883
5884   /* !!! eliminate this variable !!! */
5885   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5886
5887   if (game.explosions_delayed)
5888   {
5889     ExplodeField[ex][ey] = mode;
5890     return;
5891   }
5892
5893   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5894   {
5895     int center_element = Feld[ex][ey];
5896     int artwork_element, explosion_element;     /* set these values later */
5897
5898 #if 0
5899     /* --- This is only really needed (and now handled) in "Impact()". --- */
5900     /* do not explode moving elements that left the explode field in time */
5901     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5902         center_element == EL_EMPTY &&
5903         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5904       return;
5905 #endif
5906
5907 #if 0
5908     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5909     if (mode == EX_TYPE_NORMAL ||
5910         mode == EX_TYPE_CENTER ||
5911         mode == EX_TYPE_CROSS)
5912       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5913 #endif
5914
5915     /* remove things displayed in background while burning dynamite */
5916     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5917       Back[ex][ey] = 0;
5918
5919     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5920     {
5921       /* put moving element to center field (and let it explode there) */
5922       center_element = MovingOrBlocked2Element(ex, ey);
5923       RemoveMovingField(ex, ey);
5924       Feld[ex][ey] = center_element;
5925     }
5926
5927     /* now "center_element" is finally determined -- set related values now */
5928     artwork_element = center_element;           /* for custom player artwork */
5929     explosion_element = center_element;         /* for custom player artwork */
5930
5931     if (IS_PLAYER(ex, ey))
5932     {
5933       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5934
5935       artwork_element = stored_player[player_nr].artwork_element;
5936
5937       if (level.use_explosion_element[player_nr])
5938       {
5939         explosion_element = level.explosion_element[player_nr];
5940         artwork_element = explosion_element;
5941       }
5942     }
5943
5944 #if 1
5945     if (mode == EX_TYPE_NORMAL ||
5946         mode == EX_TYPE_CENTER ||
5947         mode == EX_TYPE_CROSS)
5948       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5949 #endif
5950
5951     last_phase = element_info[explosion_element].explosion_delay + 1;
5952
5953     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5954     {
5955       int xx = x - ex + 1;
5956       int yy = y - ey + 1;
5957       int element;
5958
5959       if (!IN_LEV_FIELD(x, y) ||
5960           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5961           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5962         continue;
5963
5964       element = Feld[x][y];
5965
5966       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5967       {
5968         element = MovingOrBlocked2Element(x, y);
5969
5970         if (!IS_EXPLOSION_PROOF(element))
5971           RemoveMovingField(x, y);
5972       }
5973
5974       /* indestructible elements can only explode in center (but not flames) */
5975       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5976                                            mode == EX_TYPE_BORDER)) ||
5977           element == EL_FLAMES)
5978         continue;
5979
5980       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5981          behaviour, for example when touching a yamyam that explodes to rocks
5982          with active deadly shield, a rock is created under the player !!! */
5983       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5984 #if 0
5985       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5986           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5987            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5988 #else
5989       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5990 #endif
5991       {
5992         if (IS_ACTIVE_BOMB(element))
5993         {
5994           /* re-activate things under the bomb like gate or penguin */
5995           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5996           Back[x][y] = 0;
5997         }
5998
5999         continue;
6000       }
6001
6002       /* save walkable background elements while explosion on same tile */
6003       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6004           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6005         Back[x][y] = element;
6006
6007       /* ignite explodable elements reached by other explosion */
6008       if (element == EL_EXPLOSION)
6009         element = Store2[x][y];
6010
6011       if (AmoebaNr[x][y] &&
6012           (element == EL_AMOEBA_FULL ||
6013            element == EL_BD_AMOEBA ||
6014            element == EL_AMOEBA_GROWING))
6015       {
6016         AmoebaCnt[AmoebaNr[x][y]]--;
6017         AmoebaCnt2[AmoebaNr[x][y]]--;
6018       }
6019
6020       RemoveField(x, y);
6021
6022       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6023       {
6024         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6025
6026         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6027
6028         if (PLAYERINFO(ex, ey)->use_murphy)
6029           Store[x][y] = EL_EMPTY;
6030       }
6031
6032       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6033          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6034       else if (ELEM_IS_PLAYER(center_element))
6035         Store[x][y] = EL_EMPTY;
6036       else if (center_element == EL_YAMYAM)
6037         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6038       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6039         Store[x][y] = element_info[center_element].content.e[xx][yy];
6040 #if 1
6041       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6042          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6043          otherwise) -- FIX THIS !!! */
6044       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6045         Store[x][y] = element_info[element].content.e[1][1];
6046 #else
6047       else if (!CAN_EXPLODE(element))
6048         Store[x][y] = element_info[element].content.e[1][1];
6049 #endif
6050       else
6051         Store[x][y] = EL_EMPTY;
6052
6053       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6054           center_element == EL_AMOEBA_TO_DIAMOND)
6055         Store2[x][y] = element;
6056
6057       Feld[x][y] = EL_EXPLOSION;
6058       GfxElement[x][y] = artwork_element;
6059
6060       ExplodePhase[x][y] = 1;
6061       ExplodeDelay[x][y] = last_phase;
6062
6063       Stop[x][y] = TRUE;
6064     }
6065
6066     if (center_element == EL_YAMYAM)
6067       game.yamyam_content_nr =
6068         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6069
6070     return;
6071   }
6072
6073   if (Stop[ex][ey])
6074     return;
6075
6076   x = ex;
6077   y = ey;
6078
6079   if (phase == 1)
6080     GfxFrame[x][y] = 0;         /* restart explosion animation */
6081
6082   last_phase = ExplodeDelay[x][y];
6083
6084   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6085
6086 #ifdef DEBUG
6087
6088   /* activate this even in non-DEBUG version until cause for crash in
6089      getGraphicAnimationFrame() (see below) is found and eliminated */
6090
6091 #endif
6092 #if 1
6093
6094 #if 1
6095   /* this can happen if the player leaves an explosion just in time */
6096   if (GfxElement[x][y] == EL_UNDEFINED)
6097     GfxElement[x][y] = EL_EMPTY;
6098 #else
6099   if (GfxElement[x][y] == EL_UNDEFINED)
6100   {
6101     printf("\n\n");
6102     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6103     printf("Explode(): This should never happen!\n");
6104     printf("\n\n");
6105
6106     GfxElement[x][y] = EL_EMPTY;
6107   }
6108 #endif
6109
6110 #endif
6111
6112   border_element = Store2[x][y];
6113   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6114     border_element = StorePlayer[x][y];
6115
6116   if (phase == element_info[border_element].ignition_delay ||
6117       phase == last_phase)
6118   {
6119     boolean border_explosion = FALSE;
6120
6121     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6122         !PLAYER_EXPLOSION_PROTECTED(x, y))
6123     {
6124       KillPlayerUnlessExplosionProtected(x, y);
6125       border_explosion = TRUE;
6126     }
6127     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6128     {
6129       Feld[x][y] = Store2[x][y];
6130       Store2[x][y] = 0;
6131       Bang(x, y);
6132       border_explosion = TRUE;
6133     }
6134     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6135     {
6136       AmoebeUmwandeln(x, y);
6137       Store2[x][y] = 0;
6138       border_explosion = TRUE;
6139     }
6140
6141     /* if an element just explodes due to another explosion (chain-reaction),
6142        do not immediately end the new explosion when it was the last frame of
6143        the explosion (as it would be done in the following "if"-statement!) */
6144     if (border_explosion && phase == last_phase)
6145       return;
6146   }
6147
6148   if (phase == last_phase)
6149   {
6150     int element;
6151
6152     element = Feld[x][y] = Store[x][y];
6153     Store[x][y] = Store2[x][y] = 0;
6154     GfxElement[x][y] = EL_UNDEFINED;
6155
6156     /* player can escape from explosions and might therefore be still alive */
6157     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6158         element <= EL_PLAYER_IS_EXPLODING_4)
6159     {
6160       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6161       int explosion_element = EL_PLAYER_1 + player_nr;
6162       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6163       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6164
6165       if (level.use_explosion_element[player_nr])
6166         explosion_element = level.explosion_element[player_nr];
6167
6168       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6169                     element_info[explosion_element].content.e[xx][yy]);
6170     }
6171
6172     /* restore probably existing indestructible background element */
6173     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6174       element = Feld[x][y] = Back[x][y];
6175     Back[x][y] = 0;
6176
6177     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6178     GfxDir[x][y] = MV_NONE;
6179     ChangeDelay[x][y] = 0;
6180     ChangePage[x][y] = -1;
6181
6182 #if USE_NEW_CUSTOM_VALUE
6183     CustomValue[x][y] = 0;
6184 #endif
6185
6186     InitField_WithBug2(x, y, FALSE);
6187
6188     TEST_DrawLevelField(x, y);
6189
6190     TestIfElementTouchesCustomElement(x, y);
6191
6192     if (GFX_CRUMBLED(element))
6193       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6194
6195     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6196       StorePlayer[x][y] = 0;
6197
6198     if (ELEM_IS_PLAYER(element))
6199       RelocatePlayer(x, y, element);
6200   }
6201   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6202   {
6203     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6204     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6205
6206     if (phase == delay)
6207       TEST_DrawLevelFieldCrumbled(x, y);
6208
6209     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6210     {
6211       DrawLevelElement(x, y, Back[x][y]);
6212       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6213     }
6214     else if (IS_WALKABLE_UNDER(Back[x][y]))
6215     {
6216       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6217       DrawLevelElementThruMask(x, y, Back[x][y]);
6218     }
6219     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6220       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6221   }
6222 }
6223
6224 void DynaExplode(int ex, int ey)
6225 {
6226   int i, j;
6227   int dynabomb_element = Feld[ex][ey];
6228   int dynabomb_size = 1;
6229   boolean dynabomb_xl = FALSE;
6230   struct PlayerInfo *player;
6231   static int xy[4][2] =
6232   {
6233     { 0, -1 },
6234     { -1, 0 },
6235     { +1, 0 },
6236     { 0, +1 }
6237   };
6238
6239   if (IS_ACTIVE_BOMB(dynabomb_element))
6240   {
6241     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6242     dynabomb_size = player->dynabomb_size;
6243     dynabomb_xl = player->dynabomb_xl;
6244     player->dynabombs_left++;
6245   }
6246
6247   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6248
6249   for (i = 0; i < NUM_DIRECTIONS; i++)
6250   {
6251     for (j = 1; j <= dynabomb_size; j++)
6252     {
6253       int x = ex + j * xy[i][0];
6254       int y = ey + j * xy[i][1];
6255       int element;
6256
6257       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6258         break;
6259
6260       element = Feld[x][y];
6261
6262       /* do not restart explosions of fields with active bombs */
6263       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6264         continue;
6265
6266       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6267
6268       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6269           !IS_DIGGABLE(element) && !dynabomb_xl)
6270         break;
6271     }
6272   }
6273 }
6274
6275 void Bang(int x, int y)
6276 {
6277   int element = MovingOrBlocked2Element(x, y);
6278   int explosion_type = EX_TYPE_NORMAL;
6279
6280   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6281   {
6282     struct PlayerInfo *player = PLAYERINFO(x, y);
6283
6284 #if USE_FIX_CE_ACTION_WITH_PLAYER
6285     element = Feld[x][y] = player->initial_element;
6286 #else
6287     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6288                             player->element_nr);
6289 #endif
6290
6291     if (level.use_explosion_element[player->index_nr])
6292     {
6293       int explosion_element = level.explosion_element[player->index_nr];
6294
6295       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6296         explosion_type = EX_TYPE_CROSS;
6297       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6298         explosion_type = EX_TYPE_CENTER;
6299     }
6300   }
6301
6302   switch (element)
6303   {
6304     case EL_BUG:
6305     case EL_SPACESHIP:
6306     case EL_BD_BUTTERFLY:
6307     case EL_BD_FIREFLY:
6308     case EL_YAMYAM:
6309     case EL_DARK_YAMYAM:
6310     case EL_ROBOT:
6311     case EL_PACMAN:
6312     case EL_MOLE:
6313       RaiseScoreElement(element);
6314       break;
6315
6316     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6317     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6318     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6319     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6320     case EL_DYNABOMB_INCREASE_NUMBER:
6321     case EL_DYNABOMB_INCREASE_SIZE:
6322     case EL_DYNABOMB_INCREASE_POWER:
6323       explosion_type = EX_TYPE_DYNA;
6324       break;
6325
6326     case EL_DC_LANDMINE:
6327 #if 0
6328     case EL_EM_EXIT_OPEN:
6329     case EL_EM_STEEL_EXIT_OPEN:
6330 #endif
6331       explosion_type = EX_TYPE_CENTER;
6332       break;
6333
6334     case EL_PENGUIN:
6335     case EL_LAMP:
6336     case EL_LAMP_ACTIVE:
6337     case EL_AMOEBA_TO_DIAMOND:
6338       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6339         explosion_type = EX_TYPE_CENTER;
6340       break;
6341
6342     default:
6343       if (element_info[element].explosion_type == EXPLODES_CROSS)
6344         explosion_type = EX_TYPE_CROSS;
6345       else if (element_info[element].explosion_type == EXPLODES_1X1)
6346         explosion_type = EX_TYPE_CENTER;
6347       break;
6348   }
6349
6350   if (explosion_type == EX_TYPE_DYNA)
6351     DynaExplode(x, y);
6352   else
6353     Explode(x, y, EX_PHASE_START, explosion_type);
6354
6355   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6356 }
6357
6358 void SplashAcid(int x, int y)
6359 {
6360   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6361       (!IN_LEV_FIELD(x - 1, y - 2) ||
6362        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6363     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6364
6365   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6366       (!IN_LEV_FIELD(x + 1, y - 2) ||
6367        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6368     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6369
6370   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6371 }
6372
6373 static void InitBeltMovement()
6374 {
6375   static int belt_base_element[4] =
6376   {
6377     EL_CONVEYOR_BELT_1_LEFT,
6378     EL_CONVEYOR_BELT_2_LEFT,
6379     EL_CONVEYOR_BELT_3_LEFT,
6380     EL_CONVEYOR_BELT_4_LEFT
6381   };
6382   static int belt_base_active_element[4] =
6383   {
6384     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6385     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6386     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6387     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6388   };
6389
6390   int x, y, i, j;
6391
6392   /* set frame order for belt animation graphic according to belt direction */
6393   for (i = 0; i < NUM_BELTS; i++)
6394   {
6395     int belt_nr = i;
6396
6397     for (j = 0; j < NUM_BELT_PARTS; j++)
6398     {
6399       int element = belt_base_active_element[belt_nr] + j;
6400       int graphic_1 = el2img(element);
6401       int graphic_2 = el2panelimg(element);
6402
6403       if (game.belt_dir[i] == MV_LEFT)
6404       {
6405         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6406         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6407       }
6408       else
6409       {
6410         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6411         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6412       }
6413     }
6414   }
6415
6416   SCAN_PLAYFIELD(x, y)
6417   {
6418     int element = Feld[x][y];
6419
6420     for (i = 0; i < NUM_BELTS; i++)
6421     {
6422       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6423       {
6424         int e_belt_nr = getBeltNrFromBeltElement(element);
6425         int belt_nr = i;
6426
6427         if (e_belt_nr == belt_nr)
6428         {
6429           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6430
6431           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6432         }
6433       }
6434     }
6435   }
6436 }
6437
6438 static void ToggleBeltSwitch(int x, int y)
6439 {
6440   static int belt_base_element[4] =
6441   {
6442     EL_CONVEYOR_BELT_1_LEFT,
6443     EL_CONVEYOR_BELT_2_LEFT,
6444     EL_CONVEYOR_BELT_3_LEFT,
6445     EL_CONVEYOR_BELT_4_LEFT
6446   };
6447   static int belt_base_active_element[4] =
6448   {
6449     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6450     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6451     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6452     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6453   };
6454   static int belt_base_switch_element[4] =
6455   {
6456     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6457     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6458     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6459     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6460   };
6461   static int belt_move_dir[4] =
6462   {
6463     MV_LEFT,
6464     MV_NONE,
6465     MV_RIGHT,
6466     MV_NONE,
6467   };
6468
6469   int element = Feld[x][y];
6470   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6471   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6472   int belt_dir = belt_move_dir[belt_dir_nr];
6473   int xx, yy, i;
6474
6475   if (!IS_BELT_SWITCH(element))
6476     return;
6477
6478   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6479   game.belt_dir[belt_nr] = belt_dir;
6480
6481   if (belt_dir_nr == 3)
6482     belt_dir_nr = 1;
6483
6484   /* set frame order for belt animation graphic according to belt direction */
6485   for (i = 0; i < NUM_BELT_PARTS; i++)
6486   {
6487     int element = belt_base_active_element[belt_nr] + i;
6488     int graphic_1 = el2img(element);
6489     int graphic_2 = el2panelimg(element);
6490
6491     if (belt_dir == MV_LEFT)
6492     {
6493       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6494       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6495     }
6496     else
6497     {
6498       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6499       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6500     }
6501   }
6502
6503   SCAN_PLAYFIELD(xx, yy)
6504   {
6505     int element = Feld[xx][yy];
6506
6507     if (IS_BELT_SWITCH(element))
6508     {
6509       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6510
6511       if (e_belt_nr == belt_nr)
6512       {
6513         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6514         TEST_DrawLevelField(xx, yy);
6515       }
6516     }
6517     else if (IS_BELT(element) && belt_dir != MV_NONE)
6518     {
6519       int e_belt_nr = getBeltNrFromBeltElement(element);
6520
6521       if (e_belt_nr == belt_nr)
6522       {
6523         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6524
6525         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6526         TEST_DrawLevelField(xx, yy);
6527       }
6528     }
6529     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6530     {
6531       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6532
6533       if (e_belt_nr == belt_nr)
6534       {
6535         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6536
6537         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6538         TEST_DrawLevelField(xx, yy);
6539       }
6540     }
6541   }
6542 }
6543
6544 static void ToggleSwitchgateSwitch(int x, int y)
6545 {
6546   int xx, yy;
6547
6548   game.switchgate_pos = !game.switchgate_pos;
6549
6550   SCAN_PLAYFIELD(xx, yy)
6551   {
6552     int element = Feld[xx][yy];
6553
6554 #if !USE_BOTH_SWITCHGATE_SWITCHES
6555     if (element == EL_SWITCHGATE_SWITCH_UP ||
6556         element == EL_SWITCHGATE_SWITCH_DOWN)
6557     {
6558       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6559       TEST_DrawLevelField(xx, yy);
6560     }
6561     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6562              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6563     {
6564       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6565       TEST_DrawLevelField(xx, yy);
6566     }
6567 #else
6568     if (element == EL_SWITCHGATE_SWITCH_UP)
6569     {
6570       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6571       TEST_DrawLevelField(xx, yy);
6572     }
6573     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6574     {
6575       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6576       TEST_DrawLevelField(xx, yy);
6577     }
6578     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6579     {
6580       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6581       TEST_DrawLevelField(xx, yy);
6582     }
6583     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6584     {
6585       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6586       TEST_DrawLevelField(xx, yy);
6587     }
6588 #endif
6589     else if (element == EL_SWITCHGATE_OPEN ||
6590              element == EL_SWITCHGATE_OPENING)
6591     {
6592       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6593
6594       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6595     }
6596     else if (element == EL_SWITCHGATE_CLOSED ||
6597              element == EL_SWITCHGATE_CLOSING)
6598     {
6599       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6600
6601       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6602     }
6603   }
6604 }
6605
6606 static int getInvisibleActiveFromInvisibleElement(int element)
6607 {
6608   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6609           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6610           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6611           element);
6612 }
6613
6614 static int getInvisibleFromInvisibleActiveElement(int element)
6615 {
6616   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6617           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6618           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6619           element);
6620 }
6621
6622 static void RedrawAllLightSwitchesAndInvisibleElements()
6623 {
6624   int x, y;
6625
6626   SCAN_PLAYFIELD(x, y)
6627   {
6628     int element = Feld[x][y];
6629
6630     if (element == EL_LIGHT_SWITCH &&
6631         game.light_time_left > 0)
6632     {
6633       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6637              game.light_time_left == 0)
6638     {
6639       Feld[x][y] = EL_LIGHT_SWITCH;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (element == EL_EMC_DRIPPER &&
6643              game.light_time_left > 0)
6644     {
6645       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6646       TEST_DrawLevelField(x, y);
6647     }
6648     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6649              game.light_time_left == 0)
6650     {
6651       Feld[x][y] = EL_EMC_DRIPPER;
6652       TEST_DrawLevelField(x, y);
6653     }
6654     else if (element == EL_INVISIBLE_STEELWALL ||
6655              element == EL_INVISIBLE_WALL ||
6656              element == EL_INVISIBLE_SAND)
6657     {
6658       if (game.light_time_left > 0)
6659         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6660
6661       TEST_DrawLevelField(x, y);
6662
6663       /* uncrumble neighbour fields, if needed */
6664       if (element == EL_INVISIBLE_SAND)
6665         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6666     }
6667     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6668              element == EL_INVISIBLE_WALL_ACTIVE ||
6669              element == EL_INVISIBLE_SAND_ACTIVE)
6670     {
6671       if (game.light_time_left == 0)
6672         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6673
6674       TEST_DrawLevelField(x, y);
6675
6676       /* re-crumble neighbour fields, if needed */
6677       if (element == EL_INVISIBLE_SAND)
6678         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6679     }
6680   }
6681 }
6682
6683 static void RedrawAllInvisibleElementsForLenses()
6684 {
6685   int x, y;
6686
6687   SCAN_PLAYFIELD(x, y)
6688   {
6689     int element = Feld[x][y];
6690
6691     if (element == EL_EMC_DRIPPER &&
6692         game.lenses_time_left > 0)
6693     {
6694       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6695       TEST_DrawLevelField(x, y);
6696     }
6697     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6698              game.lenses_time_left == 0)
6699     {
6700       Feld[x][y] = EL_EMC_DRIPPER;
6701       TEST_DrawLevelField(x, y);
6702     }
6703     else if (element == EL_INVISIBLE_STEELWALL ||
6704              element == EL_INVISIBLE_WALL ||
6705              element == EL_INVISIBLE_SAND)
6706     {
6707       if (game.lenses_time_left > 0)
6708         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6709
6710       TEST_DrawLevelField(x, y);
6711
6712       /* uncrumble neighbour fields, if needed */
6713       if (element == EL_INVISIBLE_SAND)
6714         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6715     }
6716     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6717              element == EL_INVISIBLE_WALL_ACTIVE ||
6718              element == EL_INVISIBLE_SAND_ACTIVE)
6719     {
6720       if (game.lenses_time_left == 0)
6721         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6722
6723       TEST_DrawLevelField(x, y);
6724
6725       /* re-crumble neighbour fields, if needed */
6726       if (element == EL_INVISIBLE_SAND)
6727         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6728     }
6729   }
6730 }
6731
6732 static void RedrawAllInvisibleElementsForMagnifier()
6733 {
6734   int x, y;
6735
6736   SCAN_PLAYFIELD(x, y)
6737   {
6738     int element = Feld[x][y];
6739
6740     if (element == EL_EMC_FAKE_GRASS &&
6741         game.magnify_time_left > 0)
6742     {
6743       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6744       TEST_DrawLevelField(x, y);
6745     }
6746     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6747              game.magnify_time_left == 0)
6748     {
6749       Feld[x][y] = EL_EMC_FAKE_GRASS;
6750       TEST_DrawLevelField(x, y);
6751     }
6752     else if (IS_GATE_GRAY(element) &&
6753              game.magnify_time_left > 0)
6754     {
6755       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6756                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6757                     IS_EM_GATE_GRAY(element) ?
6758                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6759                     IS_EMC_GATE_GRAY(element) ?
6760                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6761                     IS_DC_GATE_GRAY(element) ?
6762                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6763                     element);
6764       TEST_DrawLevelField(x, y);
6765     }
6766     else if (IS_GATE_GRAY_ACTIVE(element) &&
6767              game.magnify_time_left == 0)
6768     {
6769       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6770                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6771                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6772                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6773                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6774                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6775                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6776                     EL_DC_GATE_WHITE_GRAY :
6777                     element);
6778       TEST_DrawLevelField(x, y);
6779     }
6780   }
6781 }
6782
6783 static void ToggleLightSwitch(int x, int y)
6784 {
6785   int element = Feld[x][y];
6786
6787   game.light_time_left =
6788     (element == EL_LIGHT_SWITCH ?
6789      level.time_light * FRAMES_PER_SECOND : 0);
6790
6791   RedrawAllLightSwitchesAndInvisibleElements();
6792 }
6793
6794 static void ActivateTimegateSwitch(int x, int y)
6795 {
6796   int xx, yy;
6797
6798   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6799
6800   SCAN_PLAYFIELD(xx, yy)
6801   {
6802     int element = Feld[xx][yy];
6803
6804     if (element == EL_TIMEGATE_CLOSED ||
6805         element == EL_TIMEGATE_CLOSING)
6806     {
6807       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6808       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6809     }
6810
6811     /*
6812     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6813     {
6814       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6815       TEST_DrawLevelField(xx, yy);
6816     }
6817     */
6818
6819   }
6820
6821 #if 1
6822   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6823                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6824 #else
6825   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6826 #endif
6827 }
6828
6829 void Impact(int x, int y)
6830 {
6831   boolean last_line = (y == lev_fieldy - 1);
6832   boolean object_hit = FALSE;
6833   boolean impact = (last_line || object_hit);
6834   int element = Feld[x][y];
6835   int smashed = EL_STEELWALL;
6836
6837   if (!last_line)       /* check if element below was hit */
6838   {
6839     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6840       return;
6841
6842     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6843                                          MovDir[x][y + 1] != MV_DOWN ||
6844                                          MovPos[x][y + 1] <= TILEY / 2));
6845
6846     /* do not smash moving elements that left the smashed field in time */
6847     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6848         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6849       object_hit = FALSE;
6850
6851 #if USE_QUICKSAND_IMPACT_BUGFIX
6852     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6853     {
6854       RemoveMovingField(x, y + 1);
6855       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6856       Feld[x][y + 2] = EL_ROCK;
6857       TEST_DrawLevelField(x, y + 2);
6858
6859       object_hit = TRUE;
6860     }
6861
6862     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6863     {
6864       RemoveMovingField(x, y + 1);
6865       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6866       Feld[x][y + 2] = EL_ROCK;
6867       TEST_DrawLevelField(x, y + 2);
6868
6869       object_hit = TRUE;
6870     }
6871 #endif
6872
6873     if (object_hit)
6874       smashed = MovingOrBlocked2Element(x, y + 1);
6875
6876     impact = (last_line || object_hit);
6877   }
6878
6879   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6880   {
6881     SplashAcid(x, y + 1);
6882     return;
6883   }
6884
6885   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6886   /* only reset graphic animation if graphic really changes after impact */
6887   if (impact &&
6888       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6889   {
6890     ResetGfxAnimation(x, y);
6891     TEST_DrawLevelField(x, y);
6892   }
6893
6894   if (impact && CAN_EXPLODE_IMPACT(element))
6895   {
6896     Bang(x, y);
6897     return;
6898   }
6899   else if (impact && element == EL_PEARL &&
6900            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6901   {
6902     ResetGfxAnimation(x, y);
6903
6904     Feld[x][y] = EL_PEARL_BREAKING;
6905     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6906     return;
6907   }
6908   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6909   {
6910     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6911
6912     return;
6913   }
6914
6915   if (impact && element == EL_AMOEBA_DROP)
6916   {
6917     if (object_hit && IS_PLAYER(x, y + 1))
6918       KillPlayerUnlessEnemyProtected(x, y + 1);
6919     else if (object_hit && smashed == EL_PENGUIN)
6920       Bang(x, y + 1);
6921     else
6922     {
6923       Feld[x][y] = EL_AMOEBA_GROWING;
6924       Store[x][y] = EL_AMOEBA_WET;
6925
6926       ResetRandomAnimationValue(x, y);
6927     }
6928     return;
6929   }
6930
6931   if (object_hit)               /* check which object was hit */
6932   {
6933     if ((CAN_PASS_MAGIC_WALL(element) && 
6934          (smashed == EL_MAGIC_WALL ||
6935           smashed == EL_BD_MAGIC_WALL)) ||
6936         (CAN_PASS_DC_MAGIC_WALL(element) &&
6937          smashed == EL_DC_MAGIC_WALL))
6938     {
6939       int xx, yy;
6940       int activated_magic_wall =
6941         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6942          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6943          EL_DC_MAGIC_WALL_ACTIVE);
6944
6945       /* activate magic wall / mill */
6946       SCAN_PLAYFIELD(xx, yy)
6947       {
6948         if (Feld[xx][yy] == smashed)
6949           Feld[xx][yy] = activated_magic_wall;
6950       }
6951
6952       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6953       game.magic_wall_active = TRUE;
6954
6955       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6956                             SND_MAGIC_WALL_ACTIVATING :
6957                             smashed == EL_BD_MAGIC_WALL ?
6958                             SND_BD_MAGIC_WALL_ACTIVATING :
6959                             SND_DC_MAGIC_WALL_ACTIVATING));
6960     }
6961
6962     if (IS_PLAYER(x, y + 1))
6963     {
6964       if (CAN_SMASH_PLAYER(element))
6965       {
6966         KillPlayerUnlessEnemyProtected(x, y + 1);
6967         return;
6968       }
6969     }
6970     else if (smashed == EL_PENGUIN)
6971     {
6972       if (CAN_SMASH_PLAYER(element))
6973       {
6974         Bang(x, y + 1);
6975         return;
6976       }
6977     }
6978     else if (element == EL_BD_DIAMOND)
6979     {
6980       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6981       {
6982         Bang(x, y + 1);
6983         return;
6984       }
6985     }
6986     else if (((element == EL_SP_INFOTRON ||
6987                element == EL_SP_ZONK) &&
6988               (smashed == EL_SP_SNIKSNAK ||
6989                smashed == EL_SP_ELECTRON ||
6990                smashed == EL_SP_DISK_ORANGE)) ||
6991              (element == EL_SP_INFOTRON &&
6992               smashed == EL_SP_DISK_YELLOW))
6993     {
6994       Bang(x, y + 1);
6995       return;
6996     }
6997     else if (CAN_SMASH_EVERYTHING(element))
6998     {
6999       if (IS_CLASSIC_ENEMY(smashed) ||
7000           CAN_EXPLODE_SMASHED(smashed))
7001       {
7002         Bang(x, y + 1);
7003         return;
7004       }
7005       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7006       {
7007         if (smashed == EL_LAMP ||
7008             smashed == EL_LAMP_ACTIVE)
7009         {
7010           Bang(x, y + 1);
7011           return;
7012         }
7013         else if (smashed == EL_NUT)
7014         {
7015           Feld[x][y + 1] = EL_NUT_BREAKING;
7016           PlayLevelSound(x, y, SND_NUT_BREAKING);
7017           RaiseScoreElement(EL_NUT);
7018           return;
7019         }
7020         else if (smashed == EL_PEARL)
7021         {
7022           ResetGfxAnimation(x, y);
7023
7024           Feld[x][y + 1] = EL_PEARL_BREAKING;
7025           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7026           return;
7027         }
7028         else if (smashed == EL_DIAMOND)
7029         {
7030           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7031           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7032           return;
7033         }
7034         else if (IS_BELT_SWITCH(smashed))
7035         {
7036           ToggleBeltSwitch(x, y + 1);
7037         }
7038         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7039                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7040                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7041                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7042         {
7043           ToggleSwitchgateSwitch(x, y + 1);
7044         }
7045         else if (smashed == EL_LIGHT_SWITCH ||
7046                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7047         {
7048           ToggleLightSwitch(x, y + 1);
7049         }
7050         else
7051         {
7052 #if 0
7053           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7054 #endif
7055
7056           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7057
7058           CheckElementChangeBySide(x, y + 1, smashed, element,
7059                                    CE_SWITCHED, CH_SIDE_TOP);
7060           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7061                                             CH_SIDE_TOP);
7062         }
7063       }
7064       else
7065       {
7066         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7067       }
7068     }
7069   }
7070
7071   /* play sound of magic wall / mill */
7072   if (!last_line &&
7073       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7074        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7075        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7076   {
7077     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7078       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7079     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7080       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7081     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7082       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7083
7084     return;
7085   }
7086
7087   /* play sound of object that hits the ground */
7088   if (last_line || object_hit)
7089     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7090 }
7091
7092 inline static void TurnRoundExt(int x, int y)
7093 {
7094   static struct
7095   {
7096     int dx, dy;
7097   } move_xy[] =
7098   {
7099     {  0,  0 },
7100     { -1,  0 },
7101     { +1,  0 },
7102     {  0,  0 },
7103     {  0, -1 },
7104     {  0,  0 }, { 0, 0 }, { 0, 0 },
7105     {  0, +1 }
7106   };
7107   static struct
7108   {
7109     int left, right, back;
7110   } turn[] =
7111   {
7112     { 0,        0,              0        },
7113     { MV_DOWN,  MV_UP,          MV_RIGHT },
7114     { MV_UP,    MV_DOWN,        MV_LEFT  },
7115     { 0,        0,              0        },
7116     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7117     { 0,        0,              0        },
7118     { 0,        0,              0        },
7119     { 0,        0,              0        },
7120     { MV_RIGHT, MV_LEFT,        MV_UP    }
7121   };
7122
7123   int element = Feld[x][y];
7124   int move_pattern = element_info[element].move_pattern;
7125
7126   int old_move_dir = MovDir[x][y];
7127   int left_dir  = turn[old_move_dir].left;
7128   int right_dir = turn[old_move_dir].right;
7129   int back_dir  = turn[old_move_dir].back;
7130
7131   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7132   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7133   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7134   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7135
7136   int left_x  = x + left_dx,  left_y  = y + left_dy;
7137   int right_x = x + right_dx, right_y = y + right_dy;
7138   int move_x  = x + move_dx,  move_y  = y + move_dy;
7139
7140   int xx, yy;
7141
7142   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7143   {
7144     TestIfBadThingTouchesOtherBadThing(x, y);
7145
7146     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7147       MovDir[x][y] = right_dir;
7148     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7149       MovDir[x][y] = left_dir;
7150
7151     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7152       MovDelay[x][y] = 9;
7153     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7154       MovDelay[x][y] = 1;
7155   }
7156   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7157   {
7158     TestIfBadThingTouchesOtherBadThing(x, y);
7159
7160     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7161       MovDir[x][y] = left_dir;
7162     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7163       MovDir[x][y] = right_dir;
7164
7165     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7166       MovDelay[x][y] = 9;
7167     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7168       MovDelay[x][y] = 1;
7169   }
7170   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7171   {
7172     TestIfBadThingTouchesOtherBadThing(x, y);
7173
7174     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7175       MovDir[x][y] = left_dir;
7176     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7177       MovDir[x][y] = right_dir;
7178
7179     if (MovDir[x][y] != old_move_dir)
7180       MovDelay[x][y] = 9;
7181   }
7182   else if (element == EL_YAMYAM)
7183   {
7184     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7185     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7186
7187     if (can_turn_left && can_turn_right)
7188       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7189     else if (can_turn_left)
7190       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7191     else if (can_turn_right)
7192       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7193     else
7194       MovDir[x][y] = back_dir;
7195
7196     MovDelay[x][y] = 16 + 16 * RND(3);
7197   }
7198   else if (element == EL_DARK_YAMYAM)
7199   {
7200     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7201                                                          left_x, left_y);
7202     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7203                                                          right_x, right_y);
7204
7205     if (can_turn_left && can_turn_right)
7206       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7207     else if (can_turn_left)
7208       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7209     else if (can_turn_right)
7210       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7211     else
7212       MovDir[x][y] = back_dir;
7213
7214     MovDelay[x][y] = 16 + 16 * RND(3);
7215   }
7216   else if (element == EL_PACMAN)
7217   {
7218     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7219     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7220
7221     if (can_turn_left && can_turn_right)
7222       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7223     else if (can_turn_left)
7224       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7225     else if (can_turn_right)
7226       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7227     else
7228       MovDir[x][y] = back_dir;
7229
7230     MovDelay[x][y] = 6 + RND(40);
7231   }
7232   else if (element == EL_PIG)
7233   {
7234     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7235     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7236     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7237     boolean should_turn_left, should_turn_right, should_move_on;
7238     int rnd_value = 24;
7239     int rnd = RND(rnd_value);
7240
7241     should_turn_left = (can_turn_left &&
7242                         (!can_move_on ||
7243                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7244                                                    y + back_dy + left_dy)));
7245     should_turn_right = (can_turn_right &&
7246                          (!can_move_on ||
7247                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7248                                                     y + back_dy + right_dy)));
7249     should_move_on = (can_move_on &&
7250                       (!can_turn_left ||
7251                        !can_turn_right ||
7252                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7253                                                  y + move_dy + left_dy) ||
7254                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7255                                                  y + move_dy + right_dy)));
7256
7257     if (should_turn_left || should_turn_right || should_move_on)
7258     {
7259       if (should_turn_left && should_turn_right && should_move_on)
7260         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7261                         rnd < 2 * rnd_value / 3 ? right_dir :
7262                         old_move_dir);
7263       else if (should_turn_left && should_turn_right)
7264         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7265       else if (should_turn_left && should_move_on)
7266         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7267       else if (should_turn_right && should_move_on)
7268         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7269       else if (should_turn_left)
7270         MovDir[x][y] = left_dir;
7271       else if (should_turn_right)
7272         MovDir[x][y] = right_dir;
7273       else if (should_move_on)
7274         MovDir[x][y] = old_move_dir;
7275     }
7276     else if (can_move_on && rnd > rnd_value / 8)
7277       MovDir[x][y] = old_move_dir;
7278     else if (can_turn_left && can_turn_right)
7279       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7280     else if (can_turn_left && rnd > rnd_value / 8)
7281       MovDir[x][y] = left_dir;
7282     else if (can_turn_right && rnd > rnd_value/8)
7283       MovDir[x][y] = right_dir;
7284     else
7285       MovDir[x][y] = back_dir;
7286
7287     xx = x + move_xy[MovDir[x][y]].dx;
7288     yy = y + move_xy[MovDir[x][y]].dy;
7289
7290     if (!IN_LEV_FIELD(xx, yy) ||
7291         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7292       MovDir[x][y] = old_move_dir;
7293
7294     MovDelay[x][y] = 0;
7295   }
7296   else if (element == EL_DRAGON)
7297   {
7298     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7299     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7300     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7301     int rnd_value = 24;
7302     int rnd = RND(rnd_value);
7303
7304     if (can_move_on && rnd > rnd_value / 8)
7305       MovDir[x][y] = old_move_dir;
7306     else if (can_turn_left && can_turn_right)
7307       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7308     else if (can_turn_left && rnd > rnd_value / 8)
7309       MovDir[x][y] = left_dir;
7310     else if (can_turn_right && rnd > rnd_value / 8)
7311       MovDir[x][y] = right_dir;
7312     else
7313       MovDir[x][y] = back_dir;
7314
7315     xx = x + move_xy[MovDir[x][y]].dx;
7316     yy = y + move_xy[MovDir[x][y]].dy;
7317
7318     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7319       MovDir[x][y] = old_move_dir;
7320
7321     MovDelay[x][y] = 0;
7322   }
7323   else if (element == EL_MOLE)
7324   {
7325     boolean can_move_on =
7326       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7327                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7328                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7329     if (!can_move_on)
7330     {
7331       boolean can_turn_left =
7332         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7333                               IS_AMOEBOID(Feld[left_x][left_y])));
7334
7335       boolean can_turn_right =
7336         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7337                               IS_AMOEBOID(Feld[right_x][right_y])));
7338
7339       if (can_turn_left && can_turn_right)
7340         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7341       else if (can_turn_left)
7342         MovDir[x][y] = left_dir;
7343       else
7344         MovDir[x][y] = right_dir;
7345     }
7346
7347     if (MovDir[x][y] != old_move_dir)
7348       MovDelay[x][y] = 9;
7349   }
7350   else if (element == EL_BALLOON)
7351   {
7352     MovDir[x][y] = game.wind_direction;
7353     MovDelay[x][y] = 0;
7354   }
7355   else if (element == EL_SPRING)
7356   {
7357 #if USE_NEW_SPRING_BUMPER
7358     if (MovDir[x][y] & MV_HORIZONTAL)
7359     {
7360       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7361           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7362       {
7363         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7364         ResetGfxAnimation(move_x, move_y);
7365         TEST_DrawLevelField(move_x, move_y);
7366
7367         MovDir[x][y] = back_dir;
7368       }
7369       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7370                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7371         MovDir[x][y] = MV_NONE;
7372     }
7373 #else
7374     if (MovDir[x][y] & MV_HORIZONTAL &&
7375         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7376          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7377       MovDir[x][y] = MV_NONE;
7378 #endif
7379
7380     MovDelay[x][y] = 0;
7381   }
7382   else if (element == EL_ROBOT ||
7383            element == EL_SATELLITE ||
7384            element == EL_PENGUIN ||
7385            element == EL_EMC_ANDROID)
7386   {
7387     int attr_x = -1, attr_y = -1;
7388
7389     if (AllPlayersGone)
7390     {
7391       attr_x = ExitX;
7392       attr_y = ExitY;
7393     }
7394     else
7395     {
7396       int i;
7397
7398       for (i = 0; i < MAX_PLAYERS; i++)
7399       {
7400         struct PlayerInfo *player = &stored_player[i];
7401         int jx = player->jx, jy = player->jy;
7402
7403         if (!player->active)
7404           continue;
7405
7406         if (attr_x == -1 ||
7407             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7408         {
7409           attr_x = jx;
7410           attr_y = jy;
7411         }
7412       }
7413     }
7414
7415     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7416         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7417          game.engine_version < VERSION_IDENT(3,1,0,0)))
7418     {
7419       attr_x = ZX;
7420       attr_y = ZY;
7421     }
7422
7423     if (element == EL_PENGUIN)
7424     {
7425       int i;
7426       static int xy[4][2] =
7427       {
7428         { 0, -1 },
7429         { -1, 0 },
7430         { +1, 0 },
7431         { 0, +1 }
7432       };
7433
7434       for (i = 0; i < NUM_DIRECTIONS; i++)
7435       {
7436         int ex = x + xy[i][0];
7437         int ey = y + xy[i][1];
7438
7439         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7440                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7441                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7442                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7443         {
7444           attr_x = ex;
7445           attr_y = ey;
7446           break;
7447         }
7448       }
7449     }
7450
7451     MovDir[x][y] = MV_NONE;
7452     if (attr_x < x)
7453       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7454     else if (attr_x > x)
7455       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7456     if (attr_y < y)
7457       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7458     else if (attr_y > y)
7459       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7460
7461     if (element == EL_ROBOT)
7462     {
7463       int newx, newy;
7464
7465       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7466         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7467       Moving2Blocked(x, y, &newx, &newy);
7468
7469       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7470         MovDelay[x][y] = 8 + 8 * !RND(3);
7471       else
7472         MovDelay[x][y] = 16;
7473     }
7474     else if (element == EL_PENGUIN)
7475     {
7476       int newx, newy;
7477
7478       MovDelay[x][y] = 1;
7479
7480       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7481       {
7482         boolean first_horiz = RND(2);
7483         int new_move_dir = MovDir[x][y];
7484
7485         MovDir[x][y] =
7486           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7487         Moving2Blocked(x, y, &newx, &newy);
7488
7489         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7490           return;
7491
7492         MovDir[x][y] =
7493           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7494         Moving2Blocked(x, y, &newx, &newy);
7495
7496         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7497           return;
7498
7499         MovDir[x][y] = old_move_dir;
7500         return;
7501       }
7502     }
7503     else if (element == EL_SATELLITE)
7504     {
7505       int newx, newy;
7506
7507       MovDelay[x][y] = 1;
7508
7509       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7510       {
7511         boolean first_horiz = RND(2);
7512         int new_move_dir = MovDir[x][y];
7513
7514         MovDir[x][y] =
7515           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7516         Moving2Blocked(x, y, &newx, &newy);
7517
7518         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7519           return;
7520
7521         MovDir[x][y] =
7522           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7523         Moving2Blocked(x, y, &newx, &newy);
7524
7525         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7526           return;
7527
7528         MovDir[x][y] = old_move_dir;
7529         return;
7530       }
7531     }
7532     else if (element == EL_EMC_ANDROID)
7533     {
7534       static int check_pos[16] =
7535       {
7536         -1,             /*  0 => (invalid)          */
7537         7,              /*  1 => MV_LEFT            */
7538         3,              /*  2 => MV_RIGHT           */
7539         -1,             /*  3 => (invalid)          */
7540         1,              /*  4 =>            MV_UP   */
7541         0,              /*  5 => MV_LEFT  | MV_UP   */
7542         2,              /*  6 => MV_RIGHT | MV_UP   */
7543         -1,             /*  7 => (invalid)          */
7544         5,              /*  8 =>            MV_DOWN */
7545         6,              /*  9 => MV_LEFT  | MV_DOWN */
7546         4,              /* 10 => MV_RIGHT | MV_DOWN */
7547         -1,             /* 11 => (invalid)          */
7548         -1,             /* 12 => (invalid)          */
7549         -1,             /* 13 => (invalid)          */
7550         -1,             /* 14 => (invalid)          */
7551         -1,             /* 15 => (invalid)          */
7552       };
7553       static struct
7554       {
7555         int dx, dy;
7556         int dir;
7557       } check_xy[8] =
7558       {
7559         { -1, -1,       MV_LEFT  | MV_UP   },
7560         {  0, -1,                  MV_UP   },
7561         { +1, -1,       MV_RIGHT | MV_UP   },
7562         { +1,  0,       MV_RIGHT           },
7563         { +1, +1,       MV_RIGHT | MV_DOWN },
7564         {  0, +1,                  MV_DOWN },
7565         { -1, +1,       MV_LEFT  | MV_DOWN },
7566         { -1,  0,       MV_LEFT            },
7567       };
7568       int start_pos, check_order;
7569       boolean can_clone = FALSE;
7570       int i;
7571
7572       /* check if there is any free field around current position */
7573       for (i = 0; i < 8; i++)
7574       {
7575         int newx = x + check_xy[i].dx;
7576         int newy = y + check_xy[i].dy;
7577
7578         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7579         {
7580           can_clone = TRUE;
7581
7582           break;
7583         }
7584       }
7585
7586       if (can_clone)            /* randomly find an element to clone */
7587       {
7588         can_clone = FALSE;
7589
7590         start_pos = check_pos[RND(8)];
7591         check_order = (RND(2) ? -1 : +1);
7592
7593         for (i = 0; i < 8; i++)
7594         {
7595           int pos_raw = start_pos + i * check_order;
7596           int pos = (pos_raw + 8) % 8;
7597           int newx = x + check_xy[pos].dx;
7598           int newy = y + check_xy[pos].dy;
7599
7600           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7601           {
7602             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7603             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7604
7605             Store[x][y] = Feld[newx][newy];
7606
7607             can_clone = TRUE;
7608
7609             break;
7610           }
7611         }
7612       }
7613
7614       if (can_clone)            /* randomly find a direction to move */
7615       {
7616         can_clone = FALSE;
7617
7618         start_pos = check_pos[RND(8)];
7619         check_order = (RND(2) ? -1 : +1);
7620
7621         for (i = 0; i < 8; i++)
7622         {
7623           int pos_raw = start_pos + i * check_order;
7624           int pos = (pos_raw + 8) % 8;
7625           int newx = x + check_xy[pos].dx;
7626           int newy = y + check_xy[pos].dy;
7627           int new_move_dir = check_xy[pos].dir;
7628
7629           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7630           {
7631             MovDir[x][y] = new_move_dir;
7632             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7633
7634             can_clone = TRUE;
7635
7636             break;
7637           }
7638         }
7639       }
7640
7641       if (can_clone)            /* cloning and moving successful */
7642         return;
7643
7644       /* cannot clone -- try to move towards player */
7645
7646       start_pos = check_pos[MovDir[x][y] & 0x0f];
7647       check_order = (RND(2) ? -1 : +1);
7648
7649       for (i = 0; i < 3; i++)
7650       {
7651         /* first check start_pos, then previous/next or (next/previous) pos */
7652         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7653         int pos = (pos_raw + 8) % 8;
7654         int newx = x + check_xy[pos].dx;
7655         int newy = y + check_xy[pos].dy;
7656         int new_move_dir = check_xy[pos].dir;
7657
7658         if (IS_PLAYER(newx, newy))
7659           break;
7660
7661         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7662         {
7663           MovDir[x][y] = new_move_dir;
7664           MovDelay[x][y] = level.android_move_time * 8 + 1;
7665
7666           break;
7667         }
7668       }
7669     }
7670   }
7671   else if (move_pattern == MV_TURNING_LEFT ||
7672            move_pattern == MV_TURNING_RIGHT ||
7673            move_pattern == MV_TURNING_LEFT_RIGHT ||
7674            move_pattern == MV_TURNING_RIGHT_LEFT ||
7675            move_pattern == MV_TURNING_RANDOM ||
7676            move_pattern == MV_ALL_DIRECTIONS)
7677   {
7678     boolean can_turn_left =
7679       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7680     boolean can_turn_right =
7681       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7682
7683     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7684       return;
7685
7686     if (move_pattern == MV_TURNING_LEFT)
7687       MovDir[x][y] = left_dir;
7688     else if (move_pattern == MV_TURNING_RIGHT)
7689       MovDir[x][y] = right_dir;
7690     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7691       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7692     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7693       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7694     else if (move_pattern == MV_TURNING_RANDOM)
7695       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7696                       can_turn_right && !can_turn_left ? right_dir :
7697                       RND(2) ? left_dir : right_dir);
7698     else if (can_turn_left && can_turn_right)
7699       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7700     else if (can_turn_left)
7701       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7702     else if (can_turn_right)
7703       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7704     else
7705       MovDir[x][y] = back_dir;
7706
7707     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7708   }
7709   else if (move_pattern == MV_HORIZONTAL ||
7710            move_pattern == MV_VERTICAL)
7711   {
7712     if (move_pattern & old_move_dir)
7713       MovDir[x][y] = back_dir;
7714     else if (move_pattern == MV_HORIZONTAL)
7715       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7716     else if (move_pattern == MV_VERTICAL)
7717       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7718
7719     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7720   }
7721   else if (move_pattern & MV_ANY_DIRECTION)
7722   {
7723     MovDir[x][y] = move_pattern;
7724     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7725   }
7726   else if (move_pattern & MV_WIND_DIRECTION)
7727   {
7728     MovDir[x][y] = game.wind_direction;
7729     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7730   }
7731   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7732   {
7733     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7734       MovDir[x][y] = left_dir;
7735     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7736       MovDir[x][y] = right_dir;
7737
7738     if (MovDir[x][y] != old_move_dir)
7739       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7740   }
7741   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7742   {
7743     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7744       MovDir[x][y] = right_dir;
7745     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746       MovDir[x][y] = left_dir;
7747
7748     if (MovDir[x][y] != old_move_dir)
7749       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751   else if (move_pattern == MV_TOWARDS_PLAYER ||
7752            move_pattern == MV_AWAY_FROM_PLAYER)
7753   {
7754     int attr_x = -1, attr_y = -1;
7755     int newx, newy;
7756     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7757
7758     if (AllPlayersGone)
7759     {
7760       attr_x = ExitX;
7761       attr_y = ExitY;
7762     }
7763     else
7764     {
7765       int i;
7766
7767       for (i = 0; i < MAX_PLAYERS; i++)
7768       {
7769         struct PlayerInfo *player = &stored_player[i];
7770         int jx = player->jx, jy = player->jy;
7771
7772         if (!player->active)
7773           continue;
7774
7775         if (attr_x == -1 ||
7776             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7777         {
7778           attr_x = jx;
7779           attr_y = jy;
7780         }
7781       }
7782     }
7783
7784     MovDir[x][y] = MV_NONE;
7785     if (attr_x < x)
7786       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7787     else if (attr_x > x)
7788       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7789     if (attr_y < y)
7790       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7791     else if (attr_y > y)
7792       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7793
7794     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7795
7796     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7797     {
7798       boolean first_horiz = RND(2);
7799       int new_move_dir = MovDir[x][y];
7800
7801       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7802       {
7803         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7804         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7805
7806         return;
7807       }
7808
7809       MovDir[x][y] =
7810         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7811       Moving2Blocked(x, y, &newx, &newy);
7812
7813       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7814         return;
7815
7816       MovDir[x][y] =
7817         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7818       Moving2Blocked(x, y, &newx, &newy);
7819
7820       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7821         return;
7822
7823       MovDir[x][y] = old_move_dir;
7824     }
7825   }
7826   else if (move_pattern == MV_WHEN_PUSHED ||
7827            move_pattern == MV_WHEN_DROPPED)
7828   {
7829     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7830       MovDir[x][y] = MV_NONE;
7831
7832     MovDelay[x][y] = 0;
7833   }
7834   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7835   {
7836     static int test_xy[7][2] =
7837     {
7838       { 0, -1 },
7839       { -1, 0 },
7840       { +1, 0 },
7841       { 0, +1 },
7842       { 0, -1 },
7843       { -1, 0 },
7844       { +1, 0 },
7845     };
7846     static int test_dir[7] =
7847     {
7848       MV_UP,
7849       MV_LEFT,
7850       MV_RIGHT,
7851       MV_DOWN,
7852       MV_UP,
7853       MV_LEFT,
7854       MV_RIGHT,
7855     };
7856     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7857     int move_preference = -1000000;     /* start with very low preference */
7858     int new_move_dir = MV_NONE;
7859     int start_test = RND(4);
7860     int i;
7861
7862     for (i = 0; i < NUM_DIRECTIONS; i++)
7863     {
7864       int move_dir = test_dir[start_test + i];
7865       int move_dir_preference;
7866
7867       xx = x + test_xy[start_test + i][0];
7868       yy = y + test_xy[start_test + i][1];
7869
7870       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7871           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7872       {
7873         new_move_dir = move_dir;
7874
7875         break;
7876       }
7877
7878       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7879         continue;
7880
7881       move_dir_preference = -1 * RunnerVisit[xx][yy];
7882       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7883         move_dir_preference = PlayerVisit[xx][yy];
7884
7885       if (move_dir_preference > move_preference)
7886       {
7887         /* prefer field that has not been visited for the longest time */
7888         move_preference = move_dir_preference;
7889         new_move_dir = move_dir;
7890       }
7891       else if (move_dir_preference == move_preference &&
7892                move_dir == old_move_dir)
7893       {
7894         /* prefer last direction when all directions are preferred equally */
7895         move_preference = move_dir_preference;
7896         new_move_dir = move_dir;
7897       }
7898     }
7899
7900     MovDir[x][y] = new_move_dir;
7901     if (old_move_dir != new_move_dir)
7902       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7903   }
7904 }
7905
7906 static void TurnRound(int x, int y)
7907 {
7908   int direction = MovDir[x][y];
7909
7910   TurnRoundExt(x, y);
7911
7912   GfxDir[x][y] = MovDir[x][y];
7913
7914   if (direction != MovDir[x][y])
7915     GfxFrame[x][y] = 0;
7916
7917   if (MovDelay[x][y])
7918     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7919
7920   ResetGfxFrame(x, y, FALSE);
7921 }
7922
7923 static boolean JustBeingPushed(int x, int y)
7924 {
7925   int i;
7926
7927   for (i = 0; i < MAX_PLAYERS; i++)
7928   {
7929     struct PlayerInfo *player = &stored_player[i];
7930
7931     if (player->active && player->is_pushing && player->MovPos)
7932     {
7933       int next_jx = player->jx + (player->jx - player->last_jx);
7934       int next_jy = player->jy + (player->jy - player->last_jy);
7935
7936       if (x == next_jx && y == next_jy)
7937         return TRUE;
7938     }
7939   }
7940
7941   return FALSE;
7942 }
7943
7944 void StartMoving(int x, int y)
7945 {
7946   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7947   int element = Feld[x][y];
7948
7949   if (Stop[x][y])
7950     return;
7951
7952   if (MovDelay[x][y] == 0)
7953     GfxAction[x][y] = ACTION_DEFAULT;
7954
7955   if (CAN_FALL(element) && y < lev_fieldy - 1)
7956   {
7957     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7958         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7959       if (JustBeingPushed(x, y))
7960         return;
7961
7962     if (element == EL_QUICKSAND_FULL)
7963     {
7964       if (IS_FREE(x, y + 1))
7965       {
7966         InitMovingField(x, y, MV_DOWN);
7967         started_moving = TRUE;
7968
7969         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7970 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7971         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7972           Store[x][y] = EL_ROCK;
7973 #else
7974         Store[x][y] = EL_ROCK;
7975 #endif
7976
7977         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7978       }
7979       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7980       {
7981         if (!MovDelay[x][y])
7982         {
7983           MovDelay[x][y] = TILEY + 1;
7984
7985           ResetGfxAnimation(x, y);
7986           ResetGfxAnimation(x, y + 1);
7987         }
7988
7989         if (MovDelay[x][y])
7990         {
7991           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7992           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7993
7994           MovDelay[x][y]--;
7995           if (MovDelay[x][y])
7996             return;
7997         }
7998
7999         Feld[x][y] = EL_QUICKSAND_EMPTY;
8000         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8001         Store[x][y + 1] = Store[x][y];
8002         Store[x][y] = 0;
8003
8004         PlayLevelSoundAction(x, y, ACTION_FILLING);
8005       }
8006       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8007       {
8008         if (!MovDelay[x][y])
8009         {
8010           MovDelay[x][y] = TILEY + 1;
8011
8012           ResetGfxAnimation(x, y);
8013           ResetGfxAnimation(x, y + 1);
8014         }
8015
8016         if (MovDelay[x][y])
8017         {
8018           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8019           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8020
8021           MovDelay[x][y]--;
8022           if (MovDelay[x][y])
8023             return;
8024         }
8025
8026         Feld[x][y] = EL_QUICKSAND_EMPTY;
8027         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8028         Store[x][y + 1] = Store[x][y];
8029         Store[x][y] = 0;
8030
8031         PlayLevelSoundAction(x, y, ACTION_FILLING);
8032       }
8033     }
8034     else if (element == EL_QUICKSAND_FAST_FULL)
8035     {
8036       if (IS_FREE(x, y + 1))
8037       {
8038         InitMovingField(x, y, MV_DOWN);
8039         started_moving = TRUE;
8040
8041         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8042 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8043         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8044           Store[x][y] = EL_ROCK;
8045 #else
8046         Store[x][y] = EL_ROCK;
8047 #endif
8048
8049         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8050       }
8051       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8052       {
8053         if (!MovDelay[x][y])
8054         {
8055           MovDelay[x][y] = TILEY + 1;
8056
8057           ResetGfxAnimation(x, y);
8058           ResetGfxAnimation(x, y + 1);
8059         }
8060
8061         if (MovDelay[x][y])
8062         {
8063           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8064           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8065
8066           MovDelay[x][y]--;
8067           if (MovDelay[x][y])
8068             return;
8069         }
8070
8071         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8072         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8073         Store[x][y + 1] = Store[x][y];
8074         Store[x][y] = 0;
8075
8076         PlayLevelSoundAction(x, y, ACTION_FILLING);
8077       }
8078       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8079       {
8080         if (!MovDelay[x][y])
8081         {
8082           MovDelay[x][y] = TILEY + 1;
8083
8084           ResetGfxAnimation(x, y);
8085           ResetGfxAnimation(x, y + 1);
8086         }
8087
8088         if (MovDelay[x][y])
8089         {
8090           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8091           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8092
8093           MovDelay[x][y]--;
8094           if (MovDelay[x][y])
8095             return;
8096         }
8097
8098         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8099         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8100         Store[x][y + 1] = Store[x][y];
8101         Store[x][y] = 0;
8102
8103         PlayLevelSoundAction(x, y, ACTION_FILLING);
8104       }
8105     }
8106     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8107              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8108     {
8109       InitMovingField(x, y, MV_DOWN);
8110       started_moving = TRUE;
8111
8112       Feld[x][y] = EL_QUICKSAND_FILLING;
8113       Store[x][y] = element;
8114
8115       PlayLevelSoundAction(x, y, ACTION_FILLING);
8116     }
8117     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8118              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8119     {
8120       InitMovingField(x, y, MV_DOWN);
8121       started_moving = TRUE;
8122
8123       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8124       Store[x][y] = element;
8125
8126       PlayLevelSoundAction(x, y, ACTION_FILLING);
8127     }
8128     else if (element == EL_MAGIC_WALL_FULL)
8129     {
8130       if (IS_FREE(x, y + 1))
8131       {
8132         InitMovingField(x, y, MV_DOWN);
8133         started_moving = TRUE;
8134
8135         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8136         Store[x][y] = EL_CHANGED(Store[x][y]);
8137       }
8138       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8139       {
8140         if (!MovDelay[x][y])
8141           MovDelay[x][y] = TILEY / 4 + 1;
8142
8143         if (MovDelay[x][y])
8144         {
8145           MovDelay[x][y]--;
8146           if (MovDelay[x][y])
8147             return;
8148         }
8149
8150         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8151         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8152         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8153         Store[x][y] = 0;
8154       }
8155     }
8156     else if (element == EL_BD_MAGIC_WALL_FULL)
8157     {
8158       if (IS_FREE(x, y + 1))
8159       {
8160         InitMovingField(x, y, MV_DOWN);
8161         started_moving = TRUE;
8162
8163         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8164         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8165       }
8166       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8167       {
8168         if (!MovDelay[x][y])
8169           MovDelay[x][y] = TILEY / 4 + 1;
8170
8171         if (MovDelay[x][y])
8172         {
8173           MovDelay[x][y]--;
8174           if (MovDelay[x][y])
8175             return;
8176         }
8177
8178         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8179         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8180         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8181         Store[x][y] = 0;
8182       }
8183     }
8184     else if (element == EL_DC_MAGIC_WALL_FULL)
8185     {
8186       if (IS_FREE(x, y + 1))
8187       {
8188         InitMovingField(x, y, MV_DOWN);
8189         started_moving = TRUE;
8190
8191         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8192         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8193       }
8194       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8195       {
8196         if (!MovDelay[x][y])
8197           MovDelay[x][y] = TILEY / 4 + 1;
8198
8199         if (MovDelay[x][y])
8200         {
8201           MovDelay[x][y]--;
8202           if (MovDelay[x][y])
8203             return;
8204         }
8205
8206         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8207         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8208         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8209         Store[x][y] = 0;
8210       }
8211     }
8212     else if ((CAN_PASS_MAGIC_WALL(element) &&
8213               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8214                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8215              (CAN_PASS_DC_MAGIC_WALL(element) &&
8216               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8217
8218     {
8219       InitMovingField(x, y, MV_DOWN);
8220       started_moving = TRUE;
8221
8222       Feld[x][y] =
8223         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8224          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8225          EL_DC_MAGIC_WALL_FILLING);
8226       Store[x][y] = element;
8227     }
8228     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8229     {
8230       SplashAcid(x, y + 1);
8231
8232       InitMovingField(x, y, MV_DOWN);
8233       started_moving = TRUE;
8234
8235       Store[x][y] = EL_ACID;
8236     }
8237     else if (
8238 #if USE_FIX_IMPACT_COLLISION
8239              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8240               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8241 #else
8242              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8243               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8244 #endif
8245              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8246               CAN_FALL(element) && WasJustFalling[x][y] &&
8247               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8248
8249              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8250               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8251               (Feld[x][y + 1] == EL_BLOCKED)))
8252     {
8253       /* this is needed for a special case not covered by calling "Impact()"
8254          from "ContinueMoving()": if an element moves to a tile directly below
8255          another element which was just falling on that tile (which was empty
8256          in the previous frame), the falling element above would just stop
8257          instead of smashing the element below (in previous version, the above
8258          element was just checked for "moving" instead of "falling", resulting
8259          in incorrect smashes caused by horizontal movement of the above
8260          element; also, the case of the player being the element to smash was
8261          simply not covered here... :-/ ) */
8262
8263       CheckCollision[x][y] = 0;
8264       CheckImpact[x][y] = 0;
8265
8266       Impact(x, y);
8267     }
8268     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8269     {
8270       if (MovDir[x][y] == MV_NONE)
8271       {
8272         InitMovingField(x, y, MV_DOWN);
8273         started_moving = TRUE;
8274       }
8275     }
8276     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8277     {
8278       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8279         MovDir[x][y] = MV_DOWN;
8280
8281       InitMovingField(x, y, MV_DOWN);
8282       started_moving = TRUE;
8283     }
8284     else if (element == EL_AMOEBA_DROP)
8285     {
8286       Feld[x][y] = EL_AMOEBA_GROWING;
8287       Store[x][y] = EL_AMOEBA_WET;
8288     }
8289     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8290               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8291              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8292              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8293     {
8294       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8295                                 (IS_FREE(x - 1, y + 1) ||
8296                                  Feld[x - 1][y + 1] == EL_ACID));
8297       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8298                                 (IS_FREE(x + 1, y + 1) ||
8299                                  Feld[x + 1][y + 1] == EL_ACID));
8300       boolean can_fall_any  = (can_fall_left || can_fall_right);
8301       boolean can_fall_both = (can_fall_left && can_fall_right);
8302       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8303
8304 #if USE_NEW_ALL_SLIPPERY
8305       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8306       {
8307         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8308           can_fall_right = FALSE;
8309         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8310           can_fall_left = FALSE;
8311         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8312           can_fall_right = FALSE;
8313         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8314           can_fall_left = FALSE;
8315
8316         can_fall_any  = (can_fall_left || can_fall_right);
8317         can_fall_both = FALSE;
8318       }
8319 #else
8320       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8321       {
8322         if (slippery_type == SLIPPERY_ONLY_LEFT)
8323           can_fall_right = FALSE;
8324         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8325           can_fall_left = FALSE;
8326         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8327           can_fall_right = FALSE;
8328         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8329           can_fall_left = FALSE;
8330
8331         can_fall_any  = (can_fall_left || can_fall_right);
8332         can_fall_both = (can_fall_left && can_fall_right);
8333       }
8334 #endif
8335
8336 #if USE_NEW_ALL_SLIPPERY
8337 #else
8338 #if USE_NEW_SP_SLIPPERY
8339       /* !!! better use the same properties as for custom elements here !!! */
8340       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8341                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8342       {
8343         can_fall_right = FALSE;         /* slip down on left side */
8344         can_fall_both = FALSE;
8345       }
8346 #endif
8347 #endif
8348
8349 #if USE_NEW_ALL_SLIPPERY
8350       if (can_fall_both)
8351       {
8352         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8353           can_fall_right = FALSE;       /* slip down on left side */
8354         else
8355           can_fall_left = !(can_fall_right = RND(2));
8356
8357         can_fall_both = FALSE;
8358       }
8359 #else
8360       if (can_fall_both)
8361       {
8362         if (game.emulation == EMU_BOULDERDASH ||
8363             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8364           can_fall_right = FALSE;       /* slip down on left side */
8365         else
8366           can_fall_left = !(can_fall_right = RND(2));
8367
8368         can_fall_both = FALSE;
8369       }
8370 #endif
8371
8372       if (can_fall_any)
8373       {
8374         /* if not determined otherwise, prefer left side for slipping down */
8375         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8376         started_moving = TRUE;
8377       }
8378     }
8379 #if 0
8380     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8381 #else
8382     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8383 #endif
8384     {
8385       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8386       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8387       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8388       int belt_dir = game.belt_dir[belt_nr];
8389
8390       if ((belt_dir == MV_LEFT  && left_is_free) ||
8391           (belt_dir == MV_RIGHT && right_is_free))
8392       {
8393         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8394
8395         InitMovingField(x, y, belt_dir);
8396         started_moving = TRUE;
8397
8398         Pushed[x][y] = TRUE;
8399         Pushed[nextx][y] = TRUE;
8400
8401         GfxAction[x][y] = ACTION_DEFAULT;
8402       }
8403       else
8404       {
8405         MovDir[x][y] = 0;       /* if element was moving, stop it */
8406       }
8407     }
8408   }
8409
8410   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8411 #if 0
8412   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8413 #else
8414   if (CAN_MOVE(element) && !started_moving)
8415 #endif
8416   {
8417     int move_pattern = element_info[element].move_pattern;
8418     int newx, newy;
8419
8420 #if 0
8421 #if DEBUG
8422     if (MovDir[x][y] == MV_NONE)
8423     {
8424       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8425              x, y, element, element_info[element].token_name);
8426       printf("StartMoving(): This should never happen!\n");
8427     }
8428 #endif
8429 #endif
8430
8431     Moving2Blocked(x, y, &newx, &newy);
8432
8433     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8434       return;
8435
8436     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8437         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8438     {
8439       WasJustMoving[x][y] = 0;
8440       CheckCollision[x][y] = 0;
8441
8442       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8443
8444       if (Feld[x][y] != element)        /* element has changed */
8445         return;
8446     }
8447
8448     if (!MovDelay[x][y])        /* start new movement phase */
8449     {
8450       /* all objects that can change their move direction after each step
8451          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8452
8453       if (element != EL_YAMYAM &&
8454           element != EL_DARK_YAMYAM &&
8455           element != EL_PACMAN &&
8456           !(move_pattern & MV_ANY_DIRECTION) &&
8457           move_pattern != MV_TURNING_LEFT &&
8458           move_pattern != MV_TURNING_RIGHT &&
8459           move_pattern != MV_TURNING_LEFT_RIGHT &&
8460           move_pattern != MV_TURNING_RIGHT_LEFT &&
8461           move_pattern != MV_TURNING_RANDOM)
8462       {
8463         TurnRound(x, y);
8464
8465         if (MovDelay[x][y] && (element == EL_BUG ||
8466                                element == EL_SPACESHIP ||
8467                                element == EL_SP_SNIKSNAK ||
8468                                element == EL_SP_ELECTRON ||
8469                                element == EL_MOLE))
8470           TEST_DrawLevelField(x, y);
8471       }
8472     }
8473
8474     if (MovDelay[x][y])         /* wait some time before next movement */
8475     {
8476       MovDelay[x][y]--;
8477
8478       if (element == EL_ROBOT ||
8479           element == EL_YAMYAM ||
8480           element == EL_DARK_YAMYAM)
8481       {
8482         DrawLevelElementAnimationIfNeeded(x, y, element);
8483         PlayLevelSoundAction(x, y, ACTION_WAITING);
8484       }
8485       else if (element == EL_SP_ELECTRON)
8486         DrawLevelElementAnimationIfNeeded(x, y, element);
8487       else if (element == EL_DRAGON)
8488       {
8489         int i;
8490         int dir = MovDir[x][y];
8491         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8492         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8493         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8494                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8495                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8496                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8497         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8498
8499         GfxAction[x][y] = ACTION_ATTACKING;
8500
8501         if (IS_PLAYER(x, y))
8502           DrawPlayerField(x, y);
8503         else
8504           TEST_DrawLevelField(x, y);
8505
8506         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8507
8508         for (i = 1; i <= 3; i++)
8509         {
8510           int xx = x + i * dx;
8511           int yy = y + i * dy;
8512           int sx = SCREENX(xx);
8513           int sy = SCREENY(yy);
8514           int flame_graphic = graphic + (i - 1);
8515
8516           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8517             break;
8518
8519           if (MovDelay[x][y])
8520           {
8521             int flamed = MovingOrBlocked2Element(xx, yy);
8522
8523             /* !!! */
8524 #if 0
8525             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8526               Bang(xx, yy);
8527             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8528               RemoveMovingField(xx, yy);
8529             else
8530               RemoveField(xx, yy);
8531 #else
8532             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8533               Bang(xx, yy);
8534             else
8535               RemoveMovingField(xx, yy);
8536 #endif
8537
8538             ChangeDelay[xx][yy] = 0;
8539
8540             Feld[xx][yy] = EL_FLAMES;
8541
8542             if (IN_SCR_FIELD(sx, sy))
8543             {
8544               TEST_DrawLevelFieldCrumbled(xx, yy);
8545               DrawGraphic(sx, sy, flame_graphic, frame);
8546             }
8547           }
8548           else
8549           {
8550             if (Feld[xx][yy] == EL_FLAMES)
8551               Feld[xx][yy] = EL_EMPTY;
8552             TEST_DrawLevelField(xx, yy);
8553           }
8554         }
8555       }
8556
8557       if (MovDelay[x][y])       /* element still has to wait some time */
8558       {
8559         PlayLevelSoundAction(x, y, ACTION_WAITING);
8560
8561         return;
8562       }
8563     }
8564
8565     /* now make next step */
8566
8567     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8568
8569     if (DONT_COLLIDE_WITH(element) &&
8570         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8571         !PLAYER_ENEMY_PROTECTED(newx, newy))
8572     {
8573       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8574
8575       return;
8576     }
8577
8578     else if (CAN_MOVE_INTO_ACID(element) &&
8579              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8580              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8581              (MovDir[x][y] == MV_DOWN ||
8582               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8583     {
8584       SplashAcid(newx, newy);
8585       Store[x][y] = EL_ACID;
8586     }
8587     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8588     {
8589       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8590           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8591           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8592           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8593       {
8594         RemoveField(x, y);
8595         TEST_DrawLevelField(x, y);
8596
8597         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8598         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8599           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8600
8601         local_player->friends_still_needed--;
8602         if (!local_player->friends_still_needed &&
8603             !local_player->GameOver && AllPlayersGone)
8604           PlayerWins(local_player);
8605
8606         return;
8607       }
8608       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8609       {
8610         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8611           TEST_DrawLevelField(newx, newy);
8612         else
8613           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8614       }
8615       else if (!IS_FREE(newx, newy))
8616       {
8617         GfxAction[x][y] = ACTION_WAITING;
8618
8619         if (IS_PLAYER(x, y))
8620           DrawPlayerField(x, y);
8621         else
8622           TEST_DrawLevelField(x, y);
8623
8624         return;
8625       }
8626     }
8627     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8628     {
8629       if (IS_FOOD_PIG(Feld[newx][newy]))
8630       {
8631         if (IS_MOVING(newx, newy))
8632           RemoveMovingField(newx, newy);
8633         else
8634         {
8635           Feld[newx][newy] = EL_EMPTY;
8636           TEST_DrawLevelField(newx, newy);
8637         }
8638
8639         PlayLevelSound(x, y, SND_PIG_DIGGING);
8640       }
8641       else if (!IS_FREE(newx, newy))
8642       {
8643         if (IS_PLAYER(x, y))
8644           DrawPlayerField(x, y);
8645         else
8646           TEST_DrawLevelField(x, y);
8647
8648         return;
8649       }
8650     }
8651     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8652     {
8653       if (Store[x][y] != EL_EMPTY)
8654       {
8655         boolean can_clone = FALSE;
8656         int xx, yy;
8657
8658         /* check if element to clone is still there */
8659         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8660         {
8661           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8662           {
8663             can_clone = TRUE;
8664
8665             break;
8666           }
8667         }
8668
8669         /* cannot clone or target field not free anymore -- do not clone */
8670         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8671           Store[x][y] = EL_EMPTY;
8672       }
8673
8674       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8675       {
8676         if (IS_MV_DIAGONAL(MovDir[x][y]))
8677         {
8678           int diagonal_move_dir = MovDir[x][y];
8679           int stored = Store[x][y];
8680           int change_delay = 8;
8681           int graphic;
8682
8683           /* android is moving diagonally */
8684
8685           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8686
8687           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8688           GfxElement[x][y] = EL_EMC_ANDROID;
8689           GfxAction[x][y] = ACTION_SHRINKING;
8690           GfxDir[x][y] = diagonal_move_dir;
8691           ChangeDelay[x][y] = change_delay;
8692
8693           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8694                                    GfxDir[x][y]);
8695
8696           DrawLevelGraphicAnimation(x, y, graphic);
8697           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8698
8699           if (Feld[newx][newy] == EL_ACID)
8700           {
8701             SplashAcid(newx, newy);
8702
8703             return;
8704           }
8705
8706           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8707
8708           Store[newx][newy] = EL_EMC_ANDROID;
8709           GfxElement[newx][newy] = EL_EMC_ANDROID;
8710           GfxAction[newx][newy] = ACTION_GROWING;
8711           GfxDir[newx][newy] = diagonal_move_dir;
8712           ChangeDelay[newx][newy] = change_delay;
8713
8714           graphic = el_act_dir2img(GfxElement[newx][newy],
8715                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8716
8717           DrawLevelGraphicAnimation(newx, newy, graphic);
8718           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8719
8720           return;
8721         }
8722         else
8723         {
8724           Feld[newx][newy] = EL_EMPTY;
8725           TEST_DrawLevelField(newx, newy);
8726
8727           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8728         }
8729       }
8730       else if (!IS_FREE(newx, newy))
8731       {
8732 #if 0
8733         if (IS_PLAYER(x, y))
8734           DrawPlayerField(x, y);
8735         else
8736           TEST_DrawLevelField(x, y);
8737 #endif
8738
8739         return;
8740       }
8741     }
8742     else if (IS_CUSTOM_ELEMENT(element) &&
8743              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8744     {
8745 #if 1
8746       if (!DigFieldByCE(newx, newy, element))
8747         return;
8748 #else
8749       int new_element = Feld[newx][newy];
8750
8751       if (!IS_FREE(newx, newy))
8752       {
8753         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8754                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8755                       ACTION_BREAKING);
8756
8757         /* no element can dig solid indestructible elements */
8758         if (IS_INDESTRUCTIBLE(new_element) &&
8759             !IS_DIGGABLE(new_element) &&
8760             !IS_COLLECTIBLE(new_element))
8761           return;
8762
8763         if (AmoebaNr[newx][newy] &&
8764             (new_element == EL_AMOEBA_FULL ||
8765              new_element == EL_BD_AMOEBA ||
8766              new_element == EL_AMOEBA_GROWING))
8767         {
8768           AmoebaCnt[AmoebaNr[newx][newy]]--;
8769           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8770         }
8771
8772         if (IS_MOVING(newx, newy))
8773           RemoveMovingField(newx, newy);
8774         else
8775         {
8776           RemoveField(newx, newy);
8777           TEST_DrawLevelField(newx, newy);
8778         }
8779
8780         /* if digged element was about to explode, prevent the explosion */
8781         ExplodeField[newx][newy] = EX_TYPE_NONE;
8782
8783         PlayLevelSoundAction(x, y, action);
8784       }
8785
8786       Store[newx][newy] = EL_EMPTY;
8787
8788 #if 1
8789       /* this makes it possible to leave the removed element again */
8790       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8791         Store[newx][newy] = new_element;
8792 #else
8793       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8794       {
8795         int move_leave_element = element_info[element].move_leave_element;
8796
8797         /* this makes it possible to leave the removed element again */
8798         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8799                              new_element : move_leave_element);
8800       }
8801 #endif
8802
8803 #endif
8804
8805       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8806       {
8807         RunnerVisit[x][y] = FrameCounter;
8808         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8809       }
8810     }
8811     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8812     {
8813       if (!IS_FREE(newx, newy))
8814       {
8815         if (IS_PLAYER(x, y))
8816           DrawPlayerField(x, y);
8817         else
8818           TEST_DrawLevelField(x, y);
8819
8820         return;
8821       }
8822       else
8823       {
8824         boolean wanna_flame = !RND(10);
8825         int dx = newx - x, dy = newy - y;
8826         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8827         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8828         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8829                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8830         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8831                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8832
8833         if ((wanna_flame ||
8834              IS_CLASSIC_ENEMY(element1) ||
8835              IS_CLASSIC_ENEMY(element2)) &&
8836             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8837             element1 != EL_FLAMES && element2 != EL_FLAMES)
8838         {
8839           ResetGfxAnimation(x, y);
8840           GfxAction[x][y] = ACTION_ATTACKING;
8841
8842           if (IS_PLAYER(x, y))
8843             DrawPlayerField(x, y);
8844           else
8845             TEST_DrawLevelField(x, y);
8846
8847           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8848
8849           MovDelay[x][y] = 50;
8850
8851           /* !!! */
8852 #if 0
8853           RemoveField(newx, newy);
8854 #endif
8855           Feld[newx][newy] = EL_FLAMES;
8856           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8857           {
8858 #if 0
8859             RemoveField(newx1, newy1);
8860 #endif
8861             Feld[newx1][newy1] = EL_FLAMES;
8862           }
8863           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8864           {
8865 #if 0
8866             RemoveField(newx2, newy2);
8867 #endif
8868             Feld[newx2][newy2] = EL_FLAMES;
8869           }
8870
8871           return;
8872         }
8873       }
8874     }
8875     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8876              Feld[newx][newy] == EL_DIAMOND)
8877     {
8878       if (IS_MOVING(newx, newy))
8879         RemoveMovingField(newx, newy);
8880       else
8881       {
8882         Feld[newx][newy] = EL_EMPTY;
8883         TEST_DrawLevelField(newx, newy);
8884       }
8885
8886       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8887     }
8888     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8889              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8890     {
8891       if (AmoebaNr[newx][newy])
8892       {
8893         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8894         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8895             Feld[newx][newy] == EL_BD_AMOEBA)
8896           AmoebaCnt[AmoebaNr[newx][newy]]--;
8897       }
8898
8899 #if 0
8900       /* !!! test !!! */
8901       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8902       {
8903         RemoveMovingField(newx, newy);
8904       }
8905 #else
8906       if (IS_MOVING(newx, newy))
8907       {
8908         RemoveMovingField(newx, newy);
8909       }
8910 #endif
8911       else
8912       {
8913         Feld[newx][newy] = EL_EMPTY;
8914         TEST_DrawLevelField(newx, newy);
8915       }
8916
8917       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8918     }
8919     else if ((element == EL_PACMAN || element == EL_MOLE)
8920              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8921     {
8922       if (AmoebaNr[newx][newy])
8923       {
8924         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8925         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8926             Feld[newx][newy] == EL_BD_AMOEBA)
8927           AmoebaCnt[AmoebaNr[newx][newy]]--;
8928       }
8929
8930       if (element == EL_MOLE)
8931       {
8932         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8933         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8934
8935         ResetGfxAnimation(x, y);
8936         GfxAction[x][y] = ACTION_DIGGING;
8937         TEST_DrawLevelField(x, y);
8938
8939         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8940
8941         return;                         /* wait for shrinking amoeba */
8942       }
8943       else      /* element == EL_PACMAN */
8944       {
8945         Feld[newx][newy] = EL_EMPTY;
8946         TEST_DrawLevelField(newx, newy);
8947         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8948       }
8949     }
8950     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8951              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8952               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8953     {
8954       /* wait for shrinking amoeba to completely disappear */
8955       return;
8956     }
8957     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8958     {
8959       /* object was running against a wall */
8960
8961       TurnRound(x, y);
8962
8963 #if 0
8964       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8965       if (move_pattern & MV_ANY_DIRECTION &&
8966           move_pattern == MovDir[x][y])
8967       {
8968         int blocking_element =
8969           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8970
8971         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8972                                  MovDir[x][y]);
8973
8974         element = Feld[x][y];   /* element might have changed */
8975       }
8976 #endif
8977
8978       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8979         DrawLevelElementAnimation(x, y, element);
8980
8981       if (DONT_TOUCH(element))
8982         TestIfBadThingTouchesPlayer(x, y);
8983
8984       return;
8985     }
8986
8987     InitMovingField(x, y, MovDir[x][y]);
8988
8989     PlayLevelSoundAction(x, y, ACTION_MOVING);
8990   }
8991
8992   if (MovDir[x][y])
8993     ContinueMoving(x, y);
8994 }
8995
8996 void ContinueMoving(int x, int y)
8997 {
8998   int element = Feld[x][y];
8999   struct ElementInfo *ei = &element_info[element];
9000   int direction = MovDir[x][y];
9001   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9002   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9003   int newx = x + dx, newy = y + dy;
9004   int stored = Store[x][y];
9005   int stored_new = Store[newx][newy];
9006   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9007   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9008   boolean last_line = (newy == lev_fieldy - 1);
9009
9010   MovPos[x][y] += getElementMoveStepsize(x, y);
9011
9012   if (pushed_by_player) /* special case: moving object pushed by player */
9013     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9014
9015   if (ABS(MovPos[x][y]) < TILEX)
9016   {
9017 #if 0
9018     int ee = Feld[x][y];
9019     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9020     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9021
9022     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9023            x, y, ABS(MovPos[x][y]),
9024            ee, gg, ff,
9025            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9026 #endif
9027
9028     TEST_DrawLevelField(x, y);
9029
9030     return;     /* element is still moving */
9031   }
9032
9033   /* element reached destination field */
9034
9035   Feld[x][y] = EL_EMPTY;
9036   Feld[newx][newy] = element;
9037   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9038
9039   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9040   {
9041     element = Feld[newx][newy] = EL_ACID;
9042   }
9043   else if (element == EL_MOLE)
9044   {
9045     Feld[x][y] = EL_SAND;
9046
9047     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9048   }
9049   else if (element == EL_QUICKSAND_FILLING)
9050   {
9051     element = Feld[newx][newy] = get_next_element(element);
9052     Store[newx][newy] = Store[x][y];
9053   }
9054   else if (element == EL_QUICKSAND_EMPTYING)
9055   {
9056     Feld[x][y] = get_next_element(element);
9057     element = Feld[newx][newy] = Store[x][y];
9058   }
9059   else if (element == EL_QUICKSAND_FAST_FILLING)
9060   {
9061     element = Feld[newx][newy] = get_next_element(element);
9062     Store[newx][newy] = Store[x][y];
9063   }
9064   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9065   {
9066     Feld[x][y] = get_next_element(element);
9067     element = Feld[newx][newy] = Store[x][y];
9068   }
9069   else if (element == EL_MAGIC_WALL_FILLING)
9070   {
9071     element = Feld[newx][newy] = get_next_element(element);
9072     if (!game.magic_wall_active)
9073       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9074     Store[newx][newy] = Store[x][y];
9075   }
9076   else if (element == EL_MAGIC_WALL_EMPTYING)
9077   {
9078     Feld[x][y] = get_next_element(element);
9079     if (!game.magic_wall_active)
9080       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9081     element = Feld[newx][newy] = Store[x][y];
9082
9083 #if USE_NEW_CUSTOM_VALUE
9084     InitField(newx, newy, FALSE);
9085 #endif
9086   }
9087   else if (element == EL_BD_MAGIC_WALL_FILLING)
9088   {
9089     element = Feld[newx][newy] = get_next_element(element);
9090     if (!game.magic_wall_active)
9091       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9092     Store[newx][newy] = Store[x][y];
9093   }
9094   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9095   {
9096     Feld[x][y] = get_next_element(element);
9097     if (!game.magic_wall_active)
9098       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9099     element = Feld[newx][newy] = Store[x][y];
9100
9101 #if USE_NEW_CUSTOM_VALUE
9102     InitField(newx, newy, FALSE);
9103 #endif
9104   }
9105   else if (element == EL_DC_MAGIC_WALL_FILLING)
9106   {
9107     element = Feld[newx][newy] = get_next_element(element);
9108     if (!game.magic_wall_active)
9109       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9110     Store[newx][newy] = Store[x][y];
9111   }
9112   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9113   {
9114     Feld[x][y] = get_next_element(element);
9115     if (!game.magic_wall_active)
9116       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9117     element = Feld[newx][newy] = Store[x][y];
9118
9119 #if USE_NEW_CUSTOM_VALUE
9120     InitField(newx, newy, FALSE);
9121 #endif
9122   }
9123   else if (element == EL_AMOEBA_DROPPING)
9124   {
9125     Feld[x][y] = get_next_element(element);
9126     element = Feld[newx][newy] = Store[x][y];
9127   }
9128   else if (element == EL_SOKOBAN_OBJECT)
9129   {
9130     if (Back[x][y])
9131       Feld[x][y] = Back[x][y];
9132
9133     if (Back[newx][newy])
9134       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9135
9136     Back[x][y] = Back[newx][newy] = 0;
9137   }
9138
9139   Store[x][y] = EL_EMPTY;
9140   MovPos[x][y] = 0;
9141   MovDir[x][y] = 0;
9142   MovDelay[x][y] = 0;
9143
9144   MovDelay[newx][newy] = 0;
9145
9146   if (CAN_CHANGE_OR_HAS_ACTION(element))
9147   {
9148     /* copy element change control values to new field */
9149     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9150     ChangePage[newx][newy]  = ChangePage[x][y];
9151     ChangeCount[newx][newy] = ChangeCount[x][y];
9152     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9153   }
9154
9155 #if USE_NEW_CUSTOM_VALUE
9156   CustomValue[newx][newy] = CustomValue[x][y];
9157 #endif
9158
9159   ChangeDelay[x][y] = 0;
9160   ChangePage[x][y] = -1;
9161   ChangeCount[x][y] = 0;
9162   ChangeEvent[x][y] = -1;
9163
9164 #if USE_NEW_CUSTOM_VALUE
9165   CustomValue[x][y] = 0;
9166 #endif
9167
9168   /* copy animation control values to new field */
9169   GfxFrame[newx][newy]  = GfxFrame[x][y];
9170   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9171   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9172   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9173
9174   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9175
9176   /* some elements can leave other elements behind after moving */
9177 #if 1
9178   if (ei->move_leave_element != EL_EMPTY &&
9179       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9180       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9181 #else
9182   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9183       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9184       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9185 #endif
9186   {
9187     int move_leave_element = ei->move_leave_element;
9188
9189 #if 1
9190 #if 1
9191     /* this makes it possible to leave the removed element again */
9192     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9193       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9194 #else
9195     /* this makes it possible to leave the removed element again */
9196     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9197       move_leave_element = stored;
9198 #endif
9199 #else
9200     /* this makes it possible to leave the removed element again */
9201     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9202         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203       move_leave_element = stored;
9204 #endif
9205
9206     Feld[x][y] = move_leave_element;
9207
9208     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9209       MovDir[x][y] = direction;
9210
9211     InitField(x, y, FALSE);
9212
9213     if (GFX_CRUMBLED(Feld[x][y]))
9214       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9215
9216     if (ELEM_IS_PLAYER(move_leave_element))
9217       RelocatePlayer(x, y, move_leave_element);
9218   }
9219
9220   /* do this after checking for left-behind element */
9221   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9222
9223   if (!CAN_MOVE(element) ||
9224       (CAN_FALL(element) && direction == MV_DOWN &&
9225        (element == EL_SPRING ||
9226         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9227         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9228     GfxDir[x][y] = MovDir[newx][newy] = 0;
9229
9230   TEST_DrawLevelField(x, y);
9231   TEST_DrawLevelField(newx, newy);
9232
9233   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9234
9235   /* prevent pushed element from moving on in pushed direction */
9236   if (pushed_by_player && CAN_MOVE(element) &&
9237       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9238       !(element_info[element].move_pattern & direction))
9239     TurnRound(newx, newy);
9240
9241   /* prevent elements on conveyor belt from moving on in last direction */
9242   if (pushed_by_conveyor && CAN_FALL(element) &&
9243       direction & MV_HORIZONTAL)
9244     MovDir[newx][newy] = 0;
9245
9246   if (!pushed_by_player)
9247   {
9248     int nextx = newx + dx, nexty = newy + dy;
9249     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9250
9251     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9252
9253     if (CAN_FALL(element) && direction == MV_DOWN)
9254       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9255
9256     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9257       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9258
9259 #if USE_FIX_IMPACT_COLLISION
9260     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9261       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9262 #endif
9263   }
9264
9265   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9266   {
9267     TestIfBadThingTouchesPlayer(newx, newy);
9268     TestIfBadThingTouchesFriend(newx, newy);
9269
9270     if (!IS_CUSTOM_ELEMENT(element))
9271       TestIfBadThingTouchesOtherBadThing(newx, newy);
9272   }
9273   else if (element == EL_PENGUIN)
9274     TestIfFriendTouchesBadThing(newx, newy);
9275
9276   if (DONT_GET_HIT_BY(element))
9277   {
9278     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9279   }
9280
9281   /* give the player one last chance (one more frame) to move away */
9282   if (CAN_FALL(element) && direction == MV_DOWN &&
9283       (last_line || (!IS_FREE(x, newy + 1) &&
9284                      (!IS_PLAYER(x, newy + 1) ||
9285                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9286     Impact(x, newy);
9287
9288   if (pushed_by_player && !game.use_change_when_pushing_bug)
9289   {
9290     int push_side = MV_DIR_OPPOSITE(direction);
9291     struct PlayerInfo *player = PLAYERINFO(x, y);
9292
9293     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9294                                player->index_bit, push_side);
9295     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9296                                         player->index_bit, push_side);
9297   }
9298
9299   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9300     MovDelay[newx][newy] = 1;
9301
9302   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9303
9304   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9305
9306 #if 0
9307   if (ChangePage[newx][newy] != -1)             /* delayed change */
9308   {
9309     int page = ChangePage[newx][newy];
9310     struct ElementChangeInfo *change = &ei->change_page[page];
9311
9312     ChangePage[newx][newy] = -1;
9313
9314     if (change->can_change)
9315     {
9316       if (ChangeElement(newx, newy, element, page))
9317       {
9318         if (change->post_change_function)
9319           change->post_change_function(newx, newy);
9320       }
9321     }
9322
9323     if (change->has_action)
9324       ExecuteCustomElementAction(newx, newy, element, page);
9325   }
9326 #endif
9327
9328   TestIfElementHitsCustomElement(newx, newy, direction);
9329   TestIfPlayerTouchesCustomElement(newx, newy);
9330   TestIfElementTouchesCustomElement(newx, newy);
9331
9332   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9333       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9334     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9335                              MV_DIR_OPPOSITE(direction));
9336 }
9337
9338 int AmoebeNachbarNr(int ax, int ay)
9339 {
9340   int i;
9341   int element = Feld[ax][ay];
9342   int group_nr = 0;
9343   static int xy[4][2] =
9344   {
9345     { 0, -1 },
9346     { -1, 0 },
9347     { +1, 0 },
9348     { 0, +1 }
9349   };
9350
9351   for (i = 0; i < NUM_DIRECTIONS; i++)
9352   {
9353     int x = ax + xy[i][0];
9354     int y = ay + xy[i][1];
9355
9356     if (!IN_LEV_FIELD(x, y))
9357       continue;
9358
9359     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9360       group_nr = AmoebaNr[x][y];
9361   }
9362
9363   return group_nr;
9364 }
9365
9366 void AmoebenVereinigen(int ax, int ay)
9367 {
9368   int i, x, y, xx, yy;
9369   int new_group_nr = AmoebaNr[ax][ay];
9370   static int xy[4][2] =
9371   {
9372     { 0, -1 },
9373     { -1, 0 },
9374     { +1, 0 },
9375     { 0, +1 }
9376   };
9377
9378   if (new_group_nr == 0)
9379     return;
9380
9381   for (i = 0; i < NUM_DIRECTIONS; i++)
9382   {
9383     x = ax + xy[i][0];
9384     y = ay + xy[i][1];
9385
9386     if (!IN_LEV_FIELD(x, y))
9387       continue;
9388
9389     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9390          Feld[x][y] == EL_BD_AMOEBA ||
9391          Feld[x][y] == EL_AMOEBA_DEAD) &&
9392         AmoebaNr[x][y] != new_group_nr)
9393     {
9394       int old_group_nr = AmoebaNr[x][y];
9395
9396       if (old_group_nr == 0)
9397         return;
9398
9399       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9400       AmoebaCnt[old_group_nr] = 0;
9401       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9402       AmoebaCnt2[old_group_nr] = 0;
9403
9404       SCAN_PLAYFIELD(xx, yy)
9405       {
9406         if (AmoebaNr[xx][yy] == old_group_nr)
9407           AmoebaNr[xx][yy] = new_group_nr;
9408       }
9409     }
9410   }
9411 }
9412
9413 void AmoebeUmwandeln(int ax, int ay)
9414 {
9415   int i, x, y;
9416
9417   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9418   {
9419     int group_nr = AmoebaNr[ax][ay];
9420
9421 #ifdef DEBUG
9422     if (group_nr == 0)
9423     {
9424       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9425       printf("AmoebeUmwandeln(): This should never happen!\n");
9426       return;
9427     }
9428 #endif
9429
9430     SCAN_PLAYFIELD(x, y)
9431     {
9432       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9433       {
9434         AmoebaNr[x][y] = 0;
9435         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9436       }
9437     }
9438
9439     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9440                             SND_AMOEBA_TURNING_TO_GEM :
9441                             SND_AMOEBA_TURNING_TO_ROCK));
9442     Bang(ax, ay);
9443   }
9444   else
9445   {
9446     static int xy[4][2] =
9447     {
9448       { 0, -1 },
9449       { -1, 0 },
9450       { +1, 0 },
9451       { 0, +1 }
9452     };
9453
9454     for (i = 0; i < NUM_DIRECTIONS; i++)
9455     {
9456       x = ax + xy[i][0];
9457       y = ay + xy[i][1];
9458
9459       if (!IN_LEV_FIELD(x, y))
9460         continue;
9461
9462       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9463       {
9464         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9465                               SND_AMOEBA_TURNING_TO_GEM :
9466                               SND_AMOEBA_TURNING_TO_ROCK));
9467         Bang(x, y);
9468       }
9469     }
9470   }
9471 }
9472
9473 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9474 {
9475   int x, y;
9476   int group_nr = AmoebaNr[ax][ay];
9477   boolean done = FALSE;
9478
9479 #ifdef DEBUG
9480   if (group_nr == 0)
9481   {
9482     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9483     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9484     return;
9485   }
9486 #endif
9487
9488   SCAN_PLAYFIELD(x, y)
9489   {
9490     if (AmoebaNr[x][y] == group_nr &&
9491         (Feld[x][y] == EL_AMOEBA_DEAD ||
9492          Feld[x][y] == EL_BD_AMOEBA ||
9493          Feld[x][y] == EL_AMOEBA_GROWING))
9494     {
9495       AmoebaNr[x][y] = 0;
9496       Feld[x][y] = new_element;
9497       InitField(x, y, FALSE);
9498       TEST_DrawLevelField(x, y);
9499       done = TRUE;
9500     }
9501   }
9502
9503   if (done)
9504     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9505                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9506                             SND_BD_AMOEBA_TURNING_TO_GEM));
9507 }
9508
9509 void AmoebeWaechst(int x, int y)
9510 {
9511   static unsigned long sound_delay = 0;
9512   static unsigned long sound_delay_value = 0;
9513
9514   if (!MovDelay[x][y])          /* start new growing cycle */
9515   {
9516     MovDelay[x][y] = 7;
9517
9518     if (DelayReached(&sound_delay, sound_delay_value))
9519     {
9520       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9521       sound_delay_value = 30;
9522     }
9523   }
9524
9525   if (MovDelay[x][y])           /* wait some time before growing bigger */
9526   {
9527     MovDelay[x][y]--;
9528     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9529     {
9530       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9531                                            6 - MovDelay[x][y]);
9532
9533       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9534     }
9535
9536     if (!MovDelay[x][y])
9537     {
9538       Feld[x][y] = Store[x][y];
9539       Store[x][y] = 0;
9540       TEST_DrawLevelField(x, y);
9541     }
9542   }
9543 }
9544
9545 void AmoebaDisappearing(int x, int y)
9546 {
9547   static unsigned long sound_delay = 0;
9548   static unsigned long sound_delay_value = 0;
9549
9550   if (!MovDelay[x][y])          /* start new shrinking cycle */
9551   {
9552     MovDelay[x][y] = 7;
9553
9554     if (DelayReached(&sound_delay, sound_delay_value))
9555       sound_delay_value = 30;
9556   }
9557
9558   if (MovDelay[x][y])           /* wait some time before shrinking */
9559   {
9560     MovDelay[x][y]--;
9561     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9562     {
9563       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9564                                            6 - MovDelay[x][y]);
9565
9566       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9567     }
9568
9569     if (!MovDelay[x][y])
9570     {
9571       Feld[x][y] = EL_EMPTY;
9572       TEST_DrawLevelField(x, y);
9573
9574       /* don't let mole enter this field in this cycle;
9575          (give priority to objects falling to this field from above) */
9576       Stop[x][y] = TRUE;
9577     }
9578   }
9579 }
9580
9581 void AmoebeAbleger(int ax, int ay)
9582 {
9583   int i;
9584   int element = Feld[ax][ay];
9585   int graphic = el2img(element);
9586   int newax = ax, neway = ay;
9587   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9588   static int xy[4][2] =
9589   {
9590     { 0, -1 },
9591     { -1, 0 },
9592     { +1, 0 },
9593     { 0, +1 }
9594   };
9595
9596   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9597   {
9598     Feld[ax][ay] = EL_AMOEBA_DEAD;
9599     TEST_DrawLevelField(ax, ay);
9600     return;
9601   }
9602
9603   if (IS_ANIMATED(graphic))
9604     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9605
9606   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9607     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9608
9609   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9610   {
9611     MovDelay[ax][ay]--;
9612     if (MovDelay[ax][ay])
9613       return;
9614   }
9615
9616   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9617   {
9618     int start = RND(4);
9619     int x = ax + xy[start][0];
9620     int y = ay + xy[start][1];
9621
9622     if (!IN_LEV_FIELD(x, y))
9623       return;
9624
9625     if (IS_FREE(x, y) ||
9626         CAN_GROW_INTO(Feld[x][y]) ||
9627         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9628         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9629     {
9630       newax = x;
9631       neway = y;
9632     }
9633
9634     if (newax == ax && neway == ay)
9635       return;
9636   }
9637   else                          /* normal or "filled" (BD style) amoeba */
9638   {
9639     int start = RND(4);
9640     boolean waiting_for_player = FALSE;
9641
9642     for (i = 0; i < NUM_DIRECTIONS; i++)
9643     {
9644       int j = (start + i) % 4;
9645       int x = ax + xy[j][0];
9646       int y = ay + xy[j][1];
9647
9648       if (!IN_LEV_FIELD(x, y))
9649         continue;
9650
9651       if (IS_FREE(x, y) ||
9652           CAN_GROW_INTO(Feld[x][y]) ||
9653           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9654           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9655       {
9656         newax = x;
9657         neway = y;
9658         break;
9659       }
9660       else if (IS_PLAYER(x, y))
9661         waiting_for_player = TRUE;
9662     }
9663
9664     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9665     {
9666       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9667       {
9668         Feld[ax][ay] = EL_AMOEBA_DEAD;
9669         TEST_DrawLevelField(ax, ay);
9670         AmoebaCnt[AmoebaNr[ax][ay]]--;
9671
9672         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9673         {
9674           if (element == EL_AMOEBA_FULL)
9675             AmoebeUmwandeln(ax, ay);
9676           else if (element == EL_BD_AMOEBA)
9677             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9678         }
9679       }
9680       return;
9681     }
9682     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9683     {
9684       /* amoeba gets larger by growing in some direction */
9685
9686       int new_group_nr = AmoebaNr[ax][ay];
9687
9688 #ifdef DEBUG
9689   if (new_group_nr == 0)
9690   {
9691     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9692     printf("AmoebeAbleger(): This should never happen!\n");
9693     return;
9694   }
9695 #endif
9696
9697       AmoebaNr[newax][neway] = new_group_nr;
9698       AmoebaCnt[new_group_nr]++;
9699       AmoebaCnt2[new_group_nr]++;
9700
9701       /* if amoeba touches other amoeba(s) after growing, unify them */
9702       AmoebenVereinigen(newax, neway);
9703
9704       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9705       {
9706         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9707         return;
9708       }
9709     }
9710   }
9711
9712   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9713       (neway == lev_fieldy - 1 && newax != ax))
9714   {
9715     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9716     Store[newax][neway] = element;
9717   }
9718   else if (neway == ay || element == EL_EMC_DRIPPER)
9719   {
9720     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9721
9722     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9723   }
9724   else
9725   {
9726     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9727     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9728     Store[ax][ay] = EL_AMOEBA_DROP;
9729     ContinueMoving(ax, ay);
9730     return;
9731   }
9732
9733   TEST_DrawLevelField(newax, neway);
9734 }
9735
9736 void Life(int ax, int ay)
9737 {
9738   int x1, y1, x2, y2;
9739   int life_time = 40;
9740   int element = Feld[ax][ay];
9741   int graphic = el2img(element);
9742   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9743                          level.biomaze);
9744   boolean changed = FALSE;
9745
9746   if (IS_ANIMATED(graphic))
9747     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9748
9749   if (Stop[ax][ay])
9750     return;
9751
9752   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9753     MovDelay[ax][ay] = life_time;
9754
9755   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9756   {
9757     MovDelay[ax][ay]--;
9758     if (MovDelay[ax][ay])
9759       return;
9760   }
9761
9762   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9763   {
9764     int xx = ax+x1, yy = ay+y1;
9765     int nachbarn = 0;
9766
9767     if (!IN_LEV_FIELD(xx, yy))
9768       continue;
9769
9770     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9771     {
9772       int x = xx+x2, y = yy+y2;
9773
9774       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9775         continue;
9776
9777       if (((Feld[x][y] == element ||
9778             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9779            !Stop[x][y]) ||
9780           (IS_FREE(x, y) && Stop[x][y]))
9781         nachbarn++;
9782     }
9783
9784     if (xx == ax && yy == ay)           /* field in the middle */
9785     {
9786       if (nachbarn < life_parameter[0] ||
9787           nachbarn > life_parameter[1])
9788       {
9789         Feld[xx][yy] = EL_EMPTY;
9790         if (!Stop[xx][yy])
9791           TEST_DrawLevelField(xx, yy);
9792         Stop[xx][yy] = TRUE;
9793         changed = TRUE;
9794       }
9795     }
9796     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9797     {                                   /* free border field */
9798       if (nachbarn >= life_parameter[2] &&
9799           nachbarn <= life_parameter[3])
9800       {
9801         Feld[xx][yy] = element;
9802         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9803         if (!Stop[xx][yy])
9804           TEST_DrawLevelField(xx, yy);
9805         Stop[xx][yy] = TRUE;
9806         changed = TRUE;
9807       }
9808     }
9809   }
9810
9811   if (changed)
9812     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9813                    SND_GAME_OF_LIFE_GROWING);
9814 }
9815
9816 static void InitRobotWheel(int x, int y)
9817 {
9818   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9819 }
9820
9821 static void RunRobotWheel(int x, int y)
9822 {
9823   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9824 }
9825
9826 static void StopRobotWheel(int x, int y)
9827 {
9828   if (ZX == x && ZY == y)
9829   {
9830     ZX = ZY = -1;
9831
9832     game.robot_wheel_active = FALSE;
9833   }
9834 }
9835
9836 static void InitTimegateWheel(int x, int y)
9837 {
9838   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9839 }
9840
9841 static void RunTimegateWheel(int x, int y)
9842 {
9843   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9844 }
9845
9846 static void InitMagicBallDelay(int x, int y)
9847 {
9848 #if 1
9849   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9850 #else
9851   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9852 #endif
9853 }
9854
9855 static void ActivateMagicBall(int bx, int by)
9856 {
9857   int x, y;
9858
9859   if (level.ball_random)
9860   {
9861     int pos_border = RND(8);    /* select one of the eight border elements */
9862     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9863     int xx = pos_content % 3;
9864     int yy = pos_content / 3;
9865
9866     x = bx - 1 + xx;
9867     y = by - 1 + yy;
9868
9869     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9870       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9871   }
9872   else
9873   {
9874     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9875     {
9876       int xx = x - bx + 1;
9877       int yy = y - by + 1;
9878
9879       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9880         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9881     }
9882   }
9883
9884   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9885 }
9886
9887 void CheckExit(int x, int y)
9888 {
9889   if (local_player->gems_still_needed > 0 ||
9890       local_player->sokobanfields_still_needed > 0 ||
9891       local_player->lights_still_needed > 0)
9892   {
9893     int element = Feld[x][y];
9894     int graphic = el2img(element);
9895
9896     if (IS_ANIMATED(graphic))
9897       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9898
9899     return;
9900   }
9901
9902   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9903     return;
9904
9905   Feld[x][y] = EL_EXIT_OPENING;
9906
9907   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9908 }
9909
9910 void CheckExitEM(int x, int y)
9911 {
9912   if (local_player->gems_still_needed > 0 ||
9913       local_player->sokobanfields_still_needed > 0 ||
9914       local_player->lights_still_needed > 0)
9915   {
9916     int element = Feld[x][y];
9917     int graphic = el2img(element);
9918
9919     if (IS_ANIMATED(graphic))
9920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921
9922     return;
9923   }
9924
9925   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9926     return;
9927
9928   Feld[x][y] = EL_EM_EXIT_OPENING;
9929
9930   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9931 }
9932
9933 void CheckExitSteel(int x, int y)
9934 {
9935   if (local_player->gems_still_needed > 0 ||
9936       local_player->sokobanfields_still_needed > 0 ||
9937       local_player->lights_still_needed > 0)
9938   {
9939     int element = Feld[x][y];
9940     int graphic = el2img(element);
9941
9942     if (IS_ANIMATED(graphic))
9943       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9944
9945     return;
9946   }
9947
9948   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9949     return;
9950
9951   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9952
9953   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9954 }
9955
9956 void CheckExitSteelEM(int x, int y)
9957 {
9958   if (local_player->gems_still_needed > 0 ||
9959       local_player->sokobanfields_still_needed > 0 ||
9960       local_player->lights_still_needed > 0)
9961   {
9962     int element = Feld[x][y];
9963     int graphic = el2img(element);
9964
9965     if (IS_ANIMATED(graphic))
9966       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9967
9968     return;
9969   }
9970
9971   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9972     return;
9973
9974   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9975
9976   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9977 }
9978
9979 void CheckExitSP(int x, int y)
9980 {
9981   if (local_player->gems_still_needed > 0)
9982   {
9983     int element = Feld[x][y];
9984     int graphic = el2img(element);
9985
9986     if (IS_ANIMATED(graphic))
9987       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9988
9989     return;
9990   }
9991
9992   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9993     return;
9994
9995   Feld[x][y] = EL_SP_EXIT_OPENING;
9996
9997   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9998 }
9999
10000 static void CloseAllOpenTimegates()
10001 {
10002   int x, y;
10003
10004   SCAN_PLAYFIELD(x, y)
10005   {
10006     int element = Feld[x][y];
10007
10008     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10009     {
10010       Feld[x][y] = EL_TIMEGATE_CLOSING;
10011
10012       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10013     }
10014   }
10015 }
10016
10017 void DrawTwinkleOnField(int x, int y)
10018 {
10019   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10020     return;
10021
10022   if (Feld[x][y] == EL_BD_DIAMOND)
10023     return;
10024
10025   if (MovDelay[x][y] == 0)      /* next animation frame */
10026     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10027
10028   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10029   {
10030     MovDelay[x][y]--;
10031
10032     DrawLevelElementAnimation(x, y, Feld[x][y]);
10033
10034     if (MovDelay[x][y] != 0)
10035     {
10036       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10037                                            10 - MovDelay[x][y]);
10038
10039       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10040     }
10041   }
10042 }
10043
10044 void MauerWaechst(int x, int y)
10045 {
10046   int delay = 6;
10047
10048   if (!MovDelay[x][y])          /* next animation frame */
10049     MovDelay[x][y] = 3 * delay;
10050
10051   if (MovDelay[x][y])           /* wait some time before next frame */
10052   {
10053     MovDelay[x][y]--;
10054
10055     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10056     {
10057       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10058       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10059
10060       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10061     }
10062
10063     if (!MovDelay[x][y])
10064     {
10065       if (MovDir[x][y] == MV_LEFT)
10066       {
10067         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10068           TEST_DrawLevelField(x - 1, y);
10069       }
10070       else if (MovDir[x][y] == MV_RIGHT)
10071       {
10072         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10073           TEST_DrawLevelField(x + 1, y);
10074       }
10075       else if (MovDir[x][y] == MV_UP)
10076       {
10077         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10078           TEST_DrawLevelField(x, y - 1);
10079       }
10080       else
10081       {
10082         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10083           TEST_DrawLevelField(x, y + 1);
10084       }
10085
10086       Feld[x][y] = Store[x][y];
10087       Store[x][y] = 0;
10088       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10089       TEST_DrawLevelField(x, y);
10090     }
10091   }
10092 }
10093
10094 void MauerAbleger(int ax, int ay)
10095 {
10096   int element = Feld[ax][ay];
10097   int graphic = el2img(element);
10098   boolean oben_frei = FALSE, unten_frei = FALSE;
10099   boolean links_frei = FALSE, rechts_frei = FALSE;
10100   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10101   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10102   boolean new_wall = FALSE;
10103
10104   if (IS_ANIMATED(graphic))
10105     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10106
10107   if (!MovDelay[ax][ay])        /* start building new wall */
10108     MovDelay[ax][ay] = 6;
10109
10110   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10111   {
10112     MovDelay[ax][ay]--;
10113     if (MovDelay[ax][ay])
10114       return;
10115   }
10116
10117   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10118     oben_frei = TRUE;
10119   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10120     unten_frei = TRUE;
10121   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10122     links_frei = TRUE;
10123   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10124     rechts_frei = TRUE;
10125
10126   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10127       element == EL_EXPANDABLE_WALL_ANY)
10128   {
10129     if (oben_frei)
10130     {
10131       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10132       Store[ax][ay-1] = element;
10133       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10134       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10135         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10136                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10137       new_wall = TRUE;
10138     }
10139     if (unten_frei)
10140     {
10141       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10142       Store[ax][ay+1] = element;
10143       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10144       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10145         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10146                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10147       new_wall = TRUE;
10148     }
10149   }
10150
10151   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10152       element == EL_EXPANDABLE_WALL_ANY ||
10153       element == EL_EXPANDABLE_WALL ||
10154       element == EL_BD_EXPANDABLE_WALL)
10155   {
10156     if (links_frei)
10157     {
10158       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10159       Store[ax-1][ay] = element;
10160       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10161       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10162         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10163                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10164       new_wall = TRUE;
10165     }
10166
10167     if (rechts_frei)
10168     {
10169       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10170       Store[ax+1][ay] = element;
10171       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10172       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10173         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10174                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10175       new_wall = TRUE;
10176     }
10177   }
10178
10179   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10180     TEST_DrawLevelField(ax, ay);
10181
10182   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10183     oben_massiv = TRUE;
10184   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10185     unten_massiv = TRUE;
10186   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10187     links_massiv = TRUE;
10188   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10189     rechts_massiv = TRUE;
10190
10191   if (((oben_massiv && unten_massiv) ||
10192        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10193        element == EL_EXPANDABLE_WALL) &&
10194       ((links_massiv && rechts_massiv) ||
10195        element == EL_EXPANDABLE_WALL_VERTICAL))
10196     Feld[ax][ay] = EL_WALL;
10197
10198   if (new_wall)
10199     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10200 }
10201
10202 void MauerAblegerStahl(int ax, int ay)
10203 {
10204   int element = Feld[ax][ay];
10205   int graphic = el2img(element);
10206   boolean oben_frei = FALSE, unten_frei = FALSE;
10207   boolean links_frei = FALSE, rechts_frei = FALSE;
10208   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10209   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10210   boolean new_wall = FALSE;
10211
10212   if (IS_ANIMATED(graphic))
10213     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10214
10215   if (!MovDelay[ax][ay])        /* start building new wall */
10216     MovDelay[ax][ay] = 6;
10217
10218   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10219   {
10220     MovDelay[ax][ay]--;
10221     if (MovDelay[ax][ay])
10222       return;
10223   }
10224
10225   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10226     oben_frei = TRUE;
10227   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10228     unten_frei = TRUE;
10229   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10230     links_frei = TRUE;
10231   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10232     rechts_frei = TRUE;
10233
10234   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10235       element == EL_EXPANDABLE_STEELWALL_ANY)
10236   {
10237     if (oben_frei)
10238     {
10239       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10240       Store[ax][ay-1] = element;
10241       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10242       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10243         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10244                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10245       new_wall = TRUE;
10246     }
10247     if (unten_frei)
10248     {
10249       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10250       Store[ax][ay+1] = element;
10251       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10252       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10253         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10254                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10255       new_wall = TRUE;
10256     }
10257   }
10258
10259   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10260       element == EL_EXPANDABLE_STEELWALL_ANY)
10261   {
10262     if (links_frei)
10263     {
10264       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10265       Store[ax-1][ay] = element;
10266       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10267       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10268         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10269                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10270       new_wall = TRUE;
10271     }
10272
10273     if (rechts_frei)
10274     {
10275       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10276       Store[ax+1][ay] = element;
10277       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10278       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10279         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10280                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10281       new_wall = TRUE;
10282     }
10283   }
10284
10285   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10286     oben_massiv = TRUE;
10287   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10288     unten_massiv = TRUE;
10289   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10290     links_massiv = TRUE;
10291   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10292     rechts_massiv = TRUE;
10293
10294   if (((oben_massiv && unten_massiv) ||
10295        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10296       ((links_massiv && rechts_massiv) ||
10297        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10298     Feld[ax][ay] = EL_STEELWALL;
10299
10300   if (new_wall)
10301     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10302 }
10303
10304 void CheckForDragon(int x, int y)
10305 {
10306   int i, j;
10307   boolean dragon_found = FALSE;
10308   static int xy[4][2] =
10309   {
10310     { 0, -1 },
10311     { -1, 0 },
10312     { +1, 0 },
10313     { 0, +1 }
10314   };
10315
10316   for (i = 0; i < NUM_DIRECTIONS; i++)
10317   {
10318     for (j = 0; j < 4; j++)
10319     {
10320       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10321
10322       if (IN_LEV_FIELD(xx, yy) &&
10323           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10324       {
10325         if (Feld[xx][yy] == EL_DRAGON)
10326           dragon_found = TRUE;
10327       }
10328       else
10329         break;
10330     }
10331   }
10332
10333   if (!dragon_found)
10334   {
10335     for (i = 0; i < NUM_DIRECTIONS; i++)
10336     {
10337       for (j = 0; j < 3; j++)
10338       {
10339         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10340   
10341         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10342         {
10343           Feld[xx][yy] = EL_EMPTY;
10344           TEST_DrawLevelField(xx, yy);
10345         }
10346         else
10347           break;
10348       }
10349     }
10350   }
10351 }
10352
10353 static void InitBuggyBase(int x, int y)
10354 {
10355   int element = Feld[x][y];
10356   int activating_delay = FRAMES_PER_SECOND / 4;
10357
10358   ChangeDelay[x][y] =
10359     (element == EL_SP_BUGGY_BASE ?
10360      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10361      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10362      activating_delay :
10363      element == EL_SP_BUGGY_BASE_ACTIVE ?
10364      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10365 }
10366
10367 static void WarnBuggyBase(int x, int y)
10368 {
10369   int i;
10370   static int xy[4][2] =
10371   {
10372     { 0, -1 },
10373     { -1, 0 },
10374     { +1, 0 },
10375     { 0, +1 }
10376   };
10377
10378   for (i = 0; i < NUM_DIRECTIONS; i++)
10379   {
10380     int xx = x + xy[i][0];
10381     int yy = y + xy[i][1];
10382
10383     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10384     {
10385       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10386
10387       break;
10388     }
10389   }
10390 }
10391
10392 static void InitTrap(int x, int y)
10393 {
10394   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10395 }
10396
10397 static void ActivateTrap(int x, int y)
10398 {
10399   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10400 }
10401
10402 static void ChangeActiveTrap(int x, int y)
10403 {
10404   int graphic = IMG_TRAP_ACTIVE;
10405
10406   /* if new animation frame was drawn, correct crumbled sand border */
10407   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10408     TEST_DrawLevelFieldCrumbled(x, y);
10409 }
10410
10411 static int getSpecialActionElement(int element, int number, int base_element)
10412 {
10413   return (element != EL_EMPTY ? element :
10414           number != -1 ? base_element + number - 1 :
10415           EL_EMPTY);
10416 }
10417
10418 static int getModifiedActionNumber(int value_old, int operator, int operand,
10419                                    int value_min, int value_max)
10420 {
10421   int value_new = (operator == CA_MODE_SET      ? operand :
10422                    operator == CA_MODE_ADD      ? value_old + operand :
10423                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10424                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10425                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10426                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10427                    value_old);
10428
10429   return (value_new < value_min ? value_min :
10430           value_new > value_max ? value_max :
10431           value_new);
10432 }
10433
10434 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10435 {
10436   struct ElementInfo *ei = &element_info[element];
10437   struct ElementChangeInfo *change = &ei->change_page[page];
10438   int target_element = change->target_element;
10439   int action_type = change->action_type;
10440   int action_mode = change->action_mode;
10441   int action_arg = change->action_arg;
10442   int action_element = change->action_element;
10443   int i;
10444
10445   if (!change->has_action)
10446     return;
10447
10448   /* ---------- determine action paramater values -------------------------- */
10449
10450   int level_time_value =
10451     (level.time > 0 ? TimeLeft :
10452      TimePlayed);
10453
10454   int action_arg_element_raw =
10455     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10456      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10457      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10458      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10459      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10460      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10461      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10462      EL_EMPTY);
10463   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10464
10465 #if 0
10466   if (action_arg_element_raw == EL_GROUP_START)
10467     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10468 #endif
10469
10470   int action_arg_direction =
10471     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10472      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10473      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10474      change->actual_trigger_side :
10475      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10476      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10477      MV_NONE);
10478
10479   int action_arg_number_min =
10480     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10481      CA_ARG_MIN);
10482
10483   int action_arg_number_max =
10484     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10485      action_type == CA_SET_LEVEL_GEMS ? 999 :
10486      action_type == CA_SET_LEVEL_TIME ? 9999 :
10487      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10488      action_type == CA_SET_CE_VALUE ? 9999 :
10489      action_type == CA_SET_CE_SCORE ? 9999 :
10490      CA_ARG_MAX);
10491
10492   int action_arg_number_reset =
10493     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10494      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10495      action_type == CA_SET_LEVEL_TIME ? level.time :
10496      action_type == CA_SET_LEVEL_SCORE ? 0 :
10497 #if USE_NEW_CUSTOM_VALUE
10498      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10499 #else
10500      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10501 #endif
10502      action_type == CA_SET_CE_SCORE ? 0 :
10503      0);
10504
10505   int action_arg_number =
10506     (action_arg <= CA_ARG_MAX ? action_arg :
10507      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10508      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10509      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10510      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10511      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10512      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10513 #if USE_NEW_CUSTOM_VALUE
10514      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10515 #else
10516      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10517 #endif
10518      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10519      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10520      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10521      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10522      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10523      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10524      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10525      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10526      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10527      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10528      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10529      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10530      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10531      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10532      -1);
10533
10534   int action_arg_number_old =
10535     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10536      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10537      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10538      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10539      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10540      0);
10541
10542   int action_arg_number_new =
10543     getModifiedActionNumber(action_arg_number_old,
10544                             action_mode, action_arg_number,
10545                             action_arg_number_min, action_arg_number_max);
10546
10547 #if 1
10548   int trigger_player_bits =
10549     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10550      change->actual_trigger_player_bits : change->trigger_player);
10551 #else
10552   int trigger_player_bits =
10553     (change->actual_trigger_player >= EL_PLAYER_1 &&
10554      change->actual_trigger_player <= EL_PLAYER_4 ?
10555      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10556      PLAYER_BITS_ANY);
10557 #endif
10558
10559   int action_arg_player_bits =
10560     (action_arg >= CA_ARG_PLAYER_1 &&
10561      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10562      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10563      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10564      PLAYER_BITS_ANY);
10565
10566   /* ---------- execute action  -------------------------------------------- */
10567
10568   switch (action_type)
10569   {
10570     case CA_NO_ACTION:
10571     {
10572       return;
10573     }
10574
10575     /* ---------- level actions  ------------------------------------------- */
10576
10577     case CA_RESTART_LEVEL:
10578     {
10579       game.restart_level = TRUE;
10580
10581       break;
10582     }
10583
10584     case CA_SHOW_ENVELOPE:
10585     {
10586       int element = getSpecialActionElement(action_arg_element,
10587                                             action_arg_number, EL_ENVELOPE_1);
10588
10589       if (IS_ENVELOPE(element))
10590         local_player->show_envelope = element;
10591
10592       break;
10593     }
10594
10595     case CA_SET_LEVEL_TIME:
10596     {
10597       if (level.time > 0)       /* only modify limited time value */
10598       {
10599         TimeLeft = action_arg_number_new;
10600
10601 #if 1
10602         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10603
10604         DisplayGameControlValues();
10605 #else
10606         DrawGameValue_Time(TimeLeft);
10607 #endif
10608
10609         if (!TimeLeft && setup.time_limit)
10610           for (i = 0; i < MAX_PLAYERS; i++)
10611             KillPlayer(&stored_player[i]);
10612       }
10613
10614       break;
10615     }
10616
10617     case CA_SET_LEVEL_SCORE:
10618     {
10619       local_player->score = action_arg_number_new;
10620
10621 #if 1
10622       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10623
10624       DisplayGameControlValues();
10625 #else
10626       DrawGameValue_Score(local_player->score);
10627 #endif
10628
10629       break;
10630     }
10631
10632     case CA_SET_LEVEL_GEMS:
10633     {
10634       local_player->gems_still_needed = action_arg_number_new;
10635
10636 #if 1
10637       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10638
10639       DisplayGameControlValues();
10640 #else
10641       DrawGameValue_Emeralds(local_player->gems_still_needed);
10642 #endif
10643
10644       break;
10645     }
10646
10647 #if !USE_PLAYER_GRAVITY
10648     case CA_SET_LEVEL_GRAVITY:
10649     {
10650       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10651                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10652                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10653                       game.gravity);
10654       break;
10655     }
10656 #endif
10657
10658     case CA_SET_LEVEL_WIND:
10659     {
10660       game.wind_direction = action_arg_direction;
10661
10662       break;
10663     }
10664
10665     case CA_SET_LEVEL_RANDOM_SEED:
10666     {
10667 #if 1
10668       /* ensure that setting a new random seed while playing is predictable */
10669       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10670 #else
10671       InitRND(action_arg_number_new);
10672 #endif
10673
10674 #if 0
10675       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10676 #endif
10677
10678 #if 0
10679       {
10680         int i;
10681
10682         printf("::: ");
10683         for (i = 0; i < 9; i++)
10684           printf("%d, ", RND(2));
10685         printf("\n");
10686       }
10687 #endif
10688
10689       break;
10690     }
10691
10692     /* ---------- player actions  ------------------------------------------ */
10693
10694     case CA_MOVE_PLAYER:
10695     {
10696       /* automatically move to the next field in specified direction */
10697       for (i = 0; i < MAX_PLAYERS; i++)
10698         if (trigger_player_bits & (1 << i))
10699           stored_player[i].programmed_action = action_arg_direction;
10700
10701       break;
10702     }
10703
10704     case CA_EXIT_PLAYER:
10705     {
10706       for (i = 0; i < MAX_PLAYERS; i++)
10707         if (action_arg_player_bits & (1 << i))
10708           PlayerWins(&stored_player[i]);
10709
10710       break;
10711     }
10712
10713     case CA_KILL_PLAYER:
10714     {
10715       for (i = 0; i < MAX_PLAYERS; i++)
10716         if (action_arg_player_bits & (1 << i))
10717           KillPlayer(&stored_player[i]);
10718
10719       break;
10720     }
10721
10722     case CA_SET_PLAYER_KEYS:
10723     {
10724       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10725       int element = getSpecialActionElement(action_arg_element,
10726                                             action_arg_number, EL_KEY_1);
10727
10728       if (IS_KEY(element))
10729       {
10730         for (i = 0; i < MAX_PLAYERS; i++)
10731         {
10732           if (trigger_player_bits & (1 << i))
10733           {
10734             stored_player[i].key[KEY_NR(element)] = key_state;
10735
10736             DrawGameDoorValues();
10737           }
10738         }
10739       }
10740
10741       break;
10742     }
10743
10744     case CA_SET_PLAYER_SPEED:
10745     {
10746 #if 0
10747       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10748 #endif
10749
10750       for (i = 0; i < MAX_PLAYERS; i++)
10751       {
10752         if (trigger_player_bits & (1 << i))
10753         {
10754           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10755
10756           if (action_arg == CA_ARG_SPEED_FASTER &&
10757               stored_player[i].cannot_move)
10758           {
10759             action_arg_number = STEPSIZE_VERY_SLOW;
10760           }
10761           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10762                    action_arg == CA_ARG_SPEED_FASTER)
10763           {
10764             action_arg_number = 2;
10765             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10766                            CA_MODE_MULTIPLY);
10767           }
10768           else if (action_arg == CA_ARG_NUMBER_RESET)
10769           {
10770             action_arg_number = level.initial_player_stepsize[i];
10771           }
10772
10773           move_stepsize =
10774             getModifiedActionNumber(move_stepsize,
10775                                     action_mode,
10776                                     action_arg_number,
10777                                     action_arg_number_min,
10778                                     action_arg_number_max);
10779
10780           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10781         }
10782       }
10783
10784       break;
10785     }
10786
10787     case CA_SET_PLAYER_SHIELD:
10788     {
10789       for (i = 0; i < MAX_PLAYERS; i++)
10790       {
10791         if (trigger_player_bits & (1 << i))
10792         {
10793           if (action_arg == CA_ARG_SHIELD_OFF)
10794           {
10795             stored_player[i].shield_normal_time_left = 0;
10796             stored_player[i].shield_deadly_time_left = 0;
10797           }
10798           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10799           {
10800             stored_player[i].shield_normal_time_left = 999999;
10801           }
10802           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10803           {
10804             stored_player[i].shield_normal_time_left = 999999;
10805             stored_player[i].shield_deadly_time_left = 999999;
10806           }
10807         }
10808       }
10809
10810       break;
10811     }
10812
10813 #if USE_PLAYER_GRAVITY
10814     case CA_SET_PLAYER_GRAVITY:
10815     {
10816       for (i = 0; i < MAX_PLAYERS; i++)
10817       {
10818         if (trigger_player_bits & (1 << i))
10819         {
10820           stored_player[i].gravity =
10821             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10822              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10823              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10824              stored_player[i].gravity);
10825         }
10826       }
10827
10828       break;
10829     }
10830 #endif
10831
10832     case CA_SET_PLAYER_ARTWORK:
10833     {
10834       for (i = 0; i < MAX_PLAYERS; i++)
10835       {
10836         if (trigger_player_bits & (1 << i))
10837         {
10838           int artwork_element = action_arg_element;
10839
10840           if (action_arg == CA_ARG_ELEMENT_RESET)
10841             artwork_element =
10842               (level.use_artwork_element[i] ? level.artwork_element[i] :
10843                stored_player[i].element_nr);
10844
10845 #if USE_GFX_RESET_PLAYER_ARTWORK
10846           if (stored_player[i].artwork_element != artwork_element)
10847             stored_player[i].Frame = 0;
10848 #endif
10849
10850           stored_player[i].artwork_element = artwork_element;
10851
10852           SetPlayerWaiting(&stored_player[i], FALSE);
10853
10854           /* set number of special actions for bored and sleeping animation */
10855           stored_player[i].num_special_action_bored =
10856             get_num_special_action(artwork_element,
10857                                    ACTION_BORING_1, ACTION_BORING_LAST);
10858           stored_player[i].num_special_action_sleeping =
10859             get_num_special_action(artwork_element,
10860                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10861         }
10862       }
10863
10864       break;
10865     }
10866
10867     case CA_SET_PLAYER_INVENTORY:
10868     {
10869       for (i = 0; i < MAX_PLAYERS; i++)
10870       {
10871         struct PlayerInfo *player = &stored_player[i];
10872         int j, k;
10873
10874         if (trigger_player_bits & (1 << i))
10875         {
10876           int inventory_element = action_arg_element;
10877
10878           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10879               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10880               action_arg == CA_ARG_ELEMENT_ACTION)
10881           {
10882             int element = inventory_element;
10883             int collect_count = element_info[element].collect_count_initial;
10884
10885             if (!IS_CUSTOM_ELEMENT(element))
10886               collect_count = 1;
10887
10888             if (collect_count == 0)
10889               player->inventory_infinite_element = element;
10890             else
10891               for (k = 0; k < collect_count; k++)
10892                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10893                   player->inventory_element[player->inventory_size++] =
10894                     element;
10895           }
10896           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10897                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10898                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10899           {
10900             if (player->inventory_infinite_element != EL_UNDEFINED &&
10901                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10902                                      action_arg_element_raw))
10903               player->inventory_infinite_element = EL_UNDEFINED;
10904
10905             for (k = 0, j = 0; j < player->inventory_size; j++)
10906             {
10907               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10908                                         action_arg_element_raw))
10909                 player->inventory_element[k++] = player->inventory_element[j];
10910             }
10911
10912             player->inventory_size = k;
10913           }
10914           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10915           {
10916             if (player->inventory_size > 0)
10917             {
10918               for (j = 0; j < player->inventory_size - 1; j++)
10919                 player->inventory_element[j] = player->inventory_element[j + 1];
10920
10921               player->inventory_size--;
10922             }
10923           }
10924           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10925           {
10926             if (player->inventory_size > 0)
10927               player->inventory_size--;
10928           }
10929           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10930           {
10931             player->inventory_infinite_element = EL_UNDEFINED;
10932             player->inventory_size = 0;
10933           }
10934           else if (action_arg == CA_ARG_INVENTORY_RESET)
10935           {
10936             player->inventory_infinite_element = EL_UNDEFINED;
10937             player->inventory_size = 0;
10938
10939             if (level.use_initial_inventory[i])
10940             {
10941               for (j = 0; j < level.initial_inventory_size[i]; j++)
10942               {
10943                 int element = level.initial_inventory_content[i][j];
10944                 int collect_count = element_info[element].collect_count_initial;
10945
10946                 if (!IS_CUSTOM_ELEMENT(element))
10947                   collect_count = 1;
10948
10949                 if (collect_count == 0)
10950                   player->inventory_infinite_element = element;
10951                 else
10952                   for (k = 0; k < collect_count; k++)
10953                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10954                       player->inventory_element[player->inventory_size++] =
10955                         element;
10956               }
10957             }
10958           }
10959         }
10960       }
10961
10962       break;
10963     }
10964
10965     /* ---------- CE actions  ---------------------------------------------- */
10966
10967     case CA_SET_CE_VALUE:
10968     {
10969 #if USE_NEW_CUSTOM_VALUE
10970       int last_ce_value = CustomValue[x][y];
10971
10972       CustomValue[x][y] = action_arg_number_new;
10973
10974       if (CustomValue[x][y] != last_ce_value)
10975       {
10976         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10977         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10978
10979         if (CustomValue[x][y] == 0)
10980         {
10981           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10982           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10983         }
10984       }
10985 #endif
10986
10987       break;
10988     }
10989
10990     case CA_SET_CE_SCORE:
10991     {
10992 #if USE_NEW_CUSTOM_VALUE
10993       int last_ce_score = ei->collect_score;
10994
10995       ei->collect_score = action_arg_number_new;
10996
10997       if (ei->collect_score != last_ce_score)
10998       {
10999         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11000         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11001
11002         if (ei->collect_score == 0)
11003         {
11004           int xx, yy;
11005
11006           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11007           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11008
11009           /*
11010             This is a very special case that seems to be a mixture between
11011             CheckElementChange() and CheckTriggeredElementChange(): while
11012             the first one only affects single elements that are triggered
11013             directly, the second one affects multiple elements in the playfield
11014             that are triggered indirectly by another element. This is a third
11015             case: Changing the CE score always affects multiple identical CEs,
11016             so every affected CE must be checked, not only the single CE for
11017             which the CE score was changed in the first place (as every instance
11018             of that CE shares the same CE score, and therefore also can change)!
11019           */
11020           SCAN_PLAYFIELD(xx, yy)
11021           {
11022             if (Feld[xx][yy] == element)
11023               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11024                                  CE_SCORE_GETS_ZERO);
11025           }
11026         }
11027       }
11028 #endif
11029
11030       break;
11031     }
11032
11033     case CA_SET_CE_ARTWORK:
11034     {
11035       int artwork_element = action_arg_element;
11036       boolean reset_frame = FALSE;
11037       int xx, yy;
11038
11039       if (action_arg == CA_ARG_ELEMENT_RESET)
11040         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11041                            element);
11042
11043       if (ei->gfx_element != artwork_element)
11044         reset_frame = TRUE;
11045
11046       ei->gfx_element = artwork_element;
11047
11048       SCAN_PLAYFIELD(xx, yy)
11049       {
11050         if (Feld[xx][yy] == element)
11051         {
11052           if (reset_frame)
11053           {
11054             ResetGfxAnimation(xx, yy);
11055             ResetRandomAnimationValue(xx, yy);
11056           }
11057
11058           TEST_DrawLevelField(xx, yy);
11059         }
11060       }
11061
11062       break;
11063     }
11064
11065     /* ---------- engine actions  ------------------------------------------ */
11066
11067     case CA_SET_ENGINE_SCAN_MODE:
11068     {
11069       InitPlayfieldScanMode(action_arg);
11070
11071       break;
11072     }
11073
11074     default:
11075       break;
11076   }
11077 }
11078
11079 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11080 {
11081   int old_element = Feld[x][y];
11082   int new_element = GetElementFromGroupElement(element);
11083   int previous_move_direction = MovDir[x][y];
11084 #if USE_NEW_CUSTOM_VALUE
11085   int last_ce_value = CustomValue[x][y];
11086 #endif
11087   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11088   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11089   boolean add_player_onto_element = (new_element_is_player &&
11090 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11091                                      /* this breaks SnakeBite when a snake is
11092                                         halfway through a door that closes */
11093                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11094                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11095 #endif
11096                                      IS_WALKABLE(old_element));
11097
11098 #if 0
11099   /* check if element under the player changes from accessible to unaccessible
11100      (needed for special case of dropping element which then changes) */
11101   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11102       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11103   {
11104     Bang(x, y);
11105
11106     return;
11107   }
11108 #endif
11109
11110   if (!add_player_onto_element)
11111   {
11112     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11113       RemoveMovingField(x, y);
11114     else
11115       RemoveField(x, y);
11116
11117     Feld[x][y] = new_element;
11118
11119 #if !USE_GFX_RESET_GFX_ANIMATION
11120     ResetGfxAnimation(x, y);
11121     ResetRandomAnimationValue(x, y);
11122 #endif
11123
11124     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11125       MovDir[x][y] = previous_move_direction;
11126
11127 #if USE_NEW_CUSTOM_VALUE
11128     if (element_info[new_element].use_last_ce_value)
11129       CustomValue[x][y] = last_ce_value;
11130 #endif
11131
11132     InitField_WithBug1(x, y, FALSE);
11133
11134     new_element = Feld[x][y];   /* element may have changed */
11135
11136 #if USE_GFX_RESET_GFX_ANIMATION
11137     ResetGfxAnimation(x, y);
11138     ResetRandomAnimationValue(x, y);
11139 #endif
11140
11141     TEST_DrawLevelField(x, y);
11142
11143     if (GFX_CRUMBLED(new_element))
11144       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11145   }
11146
11147 #if 1
11148   /* check if element under the player changes from accessible to unaccessible
11149      (needed for special case of dropping element which then changes) */
11150   /* (must be checked after creating new element for walkable group elements) */
11151 #if USE_FIX_KILLED_BY_NON_WALKABLE
11152   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11153       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11154   {
11155     Bang(x, y);
11156
11157     return;
11158   }
11159 #else
11160   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11161       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11162   {
11163     Bang(x, y);
11164
11165     return;
11166   }
11167 #endif
11168 #endif
11169
11170   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11171   if (new_element_is_player)
11172     RelocatePlayer(x, y, new_element);
11173
11174   if (is_change)
11175     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11176
11177   TestIfBadThingTouchesPlayer(x, y);
11178   TestIfPlayerTouchesCustomElement(x, y);
11179   TestIfElementTouchesCustomElement(x, y);
11180 }
11181
11182 static void CreateField(int x, int y, int element)
11183 {
11184   CreateFieldExt(x, y, element, FALSE);
11185 }
11186
11187 static void CreateElementFromChange(int x, int y, int element)
11188 {
11189   element = GET_VALID_RUNTIME_ELEMENT(element);
11190
11191 #if USE_STOP_CHANGED_ELEMENTS
11192   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11193   {
11194     int old_element = Feld[x][y];
11195
11196     /* prevent changed element from moving in same engine frame
11197        unless both old and new element can either fall or move */
11198     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11199         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11200       Stop[x][y] = TRUE;
11201   }
11202 #endif
11203
11204   CreateFieldExt(x, y, element, TRUE);
11205 }
11206
11207 static boolean ChangeElement(int x, int y, int element, int page)
11208 {
11209   struct ElementInfo *ei = &element_info[element];
11210   struct ElementChangeInfo *change = &ei->change_page[page];
11211   int ce_value = CustomValue[x][y];
11212   int ce_score = ei->collect_score;
11213   int target_element;
11214   int old_element = Feld[x][y];
11215
11216   /* always use default change event to prevent running into a loop */
11217   if (ChangeEvent[x][y] == -1)
11218     ChangeEvent[x][y] = CE_DELAY;
11219
11220   if (ChangeEvent[x][y] == CE_DELAY)
11221   {
11222     /* reset actual trigger element, trigger player and action element */
11223     change->actual_trigger_element = EL_EMPTY;
11224     change->actual_trigger_player = EL_EMPTY;
11225     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11226     change->actual_trigger_side = CH_SIDE_NONE;
11227     change->actual_trigger_ce_value = 0;
11228     change->actual_trigger_ce_score = 0;
11229   }
11230
11231   /* do not change elements more than a specified maximum number of changes */
11232   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11233     return FALSE;
11234
11235   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11236
11237   if (change->explode)
11238   {
11239     Bang(x, y);
11240
11241     return TRUE;
11242   }
11243
11244   if (change->use_target_content)
11245   {
11246     boolean complete_replace = TRUE;
11247     boolean can_replace[3][3];
11248     int xx, yy;
11249
11250     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11251     {
11252       boolean is_empty;
11253       boolean is_walkable;
11254       boolean is_diggable;
11255       boolean is_collectible;
11256       boolean is_removable;
11257       boolean is_destructible;
11258       int ex = x + xx - 1;
11259       int ey = y + yy - 1;
11260       int content_element = change->target_content.e[xx][yy];
11261       int e;
11262
11263       can_replace[xx][yy] = TRUE;
11264
11265       if (ex == x && ey == y)   /* do not check changing element itself */
11266         continue;
11267
11268       if (content_element == EL_EMPTY_SPACE)
11269       {
11270         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11271
11272         continue;
11273       }
11274
11275       if (!IN_LEV_FIELD(ex, ey))
11276       {
11277         can_replace[xx][yy] = FALSE;
11278         complete_replace = FALSE;
11279
11280         continue;
11281       }
11282
11283       e = Feld[ex][ey];
11284
11285       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11286         e = MovingOrBlocked2Element(ex, ey);
11287
11288       is_empty = (IS_FREE(ex, ey) ||
11289                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11290
11291       is_walkable     = (is_empty || IS_WALKABLE(e));
11292       is_diggable     = (is_empty || IS_DIGGABLE(e));
11293       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11294       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11295       is_removable    = (is_diggable || is_collectible);
11296
11297       can_replace[xx][yy] =
11298         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11299           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11300           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11301           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11302           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11303           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11304          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11305
11306       if (!can_replace[xx][yy])
11307         complete_replace = FALSE;
11308     }
11309
11310     if (!change->only_if_complete || complete_replace)
11311     {
11312       boolean something_has_changed = FALSE;
11313
11314       if (change->only_if_complete && change->use_random_replace &&
11315           RND(100) < change->random_percentage)
11316         return FALSE;
11317
11318       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11319       {
11320         int ex = x + xx - 1;
11321         int ey = y + yy - 1;
11322         int content_element;
11323
11324         if (can_replace[xx][yy] && (!change->use_random_replace ||
11325                                     RND(100) < change->random_percentage))
11326         {
11327           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11328             RemoveMovingField(ex, ey);
11329
11330           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11331
11332           content_element = change->target_content.e[xx][yy];
11333           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11334                                               ce_value, ce_score);
11335
11336           CreateElementFromChange(ex, ey, target_element);
11337
11338           something_has_changed = TRUE;
11339
11340           /* for symmetry reasons, freeze newly created border elements */
11341           if (ex != x || ey != y)
11342             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11343         }
11344       }
11345
11346       if (something_has_changed)
11347       {
11348         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11349         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11350       }
11351     }
11352   }
11353   else
11354   {
11355     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11356                                         ce_value, ce_score);
11357
11358     if (element == EL_DIAGONAL_GROWING ||
11359         element == EL_DIAGONAL_SHRINKING)
11360     {
11361       target_element = Store[x][y];
11362
11363       Store[x][y] = EL_EMPTY;
11364     }
11365
11366     CreateElementFromChange(x, y, target_element);
11367
11368     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11369     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11370   }
11371
11372   /* this uses direct change before indirect change */
11373   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11374
11375   return TRUE;
11376 }
11377
11378 #if USE_NEW_DELAYED_ACTION
11379
11380 static void HandleElementChange(int x, int y, int page)
11381 {
11382   int element = MovingOrBlocked2Element(x, y);
11383   struct ElementInfo *ei = &element_info[element];
11384   struct ElementChangeInfo *change = &ei->change_page[page];
11385   boolean handle_action_before_change = FALSE;
11386
11387 #ifdef DEBUG
11388   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11389       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11390   {
11391     printf("\n\n");
11392     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11393            x, y, element, element_info[element].token_name);
11394     printf("HandleElementChange(): This should never happen!\n");
11395     printf("\n\n");
11396   }
11397 #endif
11398
11399   /* this can happen with classic bombs on walkable, changing elements */
11400   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11401   {
11402 #if 0
11403     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11404       ChangeDelay[x][y] = 0;
11405 #endif
11406
11407     return;
11408   }
11409
11410   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11411   {
11412     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11413
11414     if (change->can_change)
11415     {
11416 #if 1
11417       /* !!! not clear why graphic animation should be reset at all here !!! */
11418       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11419 #if USE_GFX_RESET_WHEN_NOT_MOVING
11420       /* when a custom element is about to change (for example by change delay),
11421          do not reset graphic animation when the custom element is moving */
11422       if (!IS_MOVING(x, y))
11423 #endif
11424       {
11425         ResetGfxAnimation(x, y);
11426         ResetRandomAnimationValue(x, y);
11427       }
11428 #endif
11429
11430       if (change->pre_change_function)
11431         change->pre_change_function(x, y);
11432     }
11433   }
11434
11435   ChangeDelay[x][y]--;
11436
11437   if (ChangeDelay[x][y] != 0)           /* continue element change */
11438   {
11439     if (change->can_change)
11440     {
11441       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11442
11443       if (IS_ANIMATED(graphic))
11444         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11445
11446       if (change->change_function)
11447         change->change_function(x, y);
11448     }
11449   }
11450   else                                  /* finish element change */
11451   {
11452     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11453     {
11454       page = ChangePage[x][y];
11455       ChangePage[x][y] = -1;
11456
11457       change = &ei->change_page[page];
11458     }
11459
11460     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11461     {
11462       ChangeDelay[x][y] = 1;            /* try change after next move step */
11463       ChangePage[x][y] = page;          /* remember page to use for change */
11464
11465       return;
11466     }
11467
11468 #if 1
11469     /* special case: set new level random seed before changing element */
11470     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11471       handle_action_before_change = TRUE;
11472
11473     if (change->has_action && handle_action_before_change)
11474       ExecuteCustomElementAction(x, y, element, page);
11475 #endif
11476
11477     if (change->can_change)
11478     {
11479       if (ChangeElement(x, y, element, page))
11480       {
11481         if (change->post_change_function)
11482           change->post_change_function(x, y);
11483       }
11484     }
11485
11486     if (change->has_action && !handle_action_before_change)
11487       ExecuteCustomElementAction(x, y, element, page);
11488   }
11489 }
11490
11491 #else
11492
11493 static void HandleElementChange(int x, int y, int page)
11494 {
11495   int element = MovingOrBlocked2Element(x, y);
11496   struct ElementInfo *ei = &element_info[element];
11497   struct ElementChangeInfo *change = &ei->change_page[page];
11498
11499 #ifdef DEBUG
11500   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11501   {
11502     printf("\n\n");
11503     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11504            x, y, element, element_info[element].token_name);
11505     printf("HandleElementChange(): This should never happen!\n");
11506     printf("\n\n");
11507   }
11508 #endif
11509
11510   /* this can happen with classic bombs on walkable, changing elements */
11511   if (!CAN_CHANGE(element))
11512   {
11513 #if 0
11514     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11515       ChangeDelay[x][y] = 0;
11516 #endif
11517
11518     return;
11519   }
11520
11521   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11522   {
11523     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11524
11525     ResetGfxAnimation(x, y);
11526     ResetRandomAnimationValue(x, y);
11527
11528     if (change->pre_change_function)
11529       change->pre_change_function(x, y);
11530   }
11531
11532   ChangeDelay[x][y]--;
11533
11534   if (ChangeDelay[x][y] != 0)           /* continue element change */
11535   {
11536     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11537
11538     if (IS_ANIMATED(graphic))
11539       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11540
11541     if (change->change_function)
11542       change->change_function(x, y);
11543   }
11544   else                                  /* finish element change */
11545   {
11546     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11547     {
11548       page = ChangePage[x][y];
11549       ChangePage[x][y] = -1;
11550
11551       change = &ei->change_page[page];
11552     }
11553
11554     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11555     {
11556       ChangeDelay[x][y] = 1;            /* try change after next move step */
11557       ChangePage[x][y] = page;          /* remember page to use for change */
11558
11559       return;
11560     }
11561
11562     if (ChangeElement(x, y, element, page))
11563     {
11564       if (change->post_change_function)
11565         change->post_change_function(x, y);
11566     }
11567   }
11568 }
11569
11570 #endif
11571
11572 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11573                                               int trigger_element,
11574                                               int trigger_event,
11575                                               int trigger_player,
11576                                               int trigger_side,
11577                                               int trigger_page)
11578 {
11579   boolean change_done_any = FALSE;
11580   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11581   int i;
11582
11583   if (!(trigger_events[trigger_element][trigger_event]))
11584     return FALSE;
11585
11586 #if 0
11587   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11588          trigger_event, recursion_loop_depth, recursion_loop_detected,
11589          recursion_loop_element, EL_NAME(recursion_loop_element));
11590 #endif
11591
11592   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11593
11594   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11595   {
11596     int element = EL_CUSTOM_START + i;
11597     boolean change_done = FALSE;
11598     int p;
11599
11600     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11601         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11602       continue;
11603
11604     for (p = 0; p < element_info[element].num_change_pages; p++)
11605     {
11606       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11607
11608       if (change->can_change_or_has_action &&
11609           change->has_event[trigger_event] &&
11610           change->trigger_side & trigger_side &&
11611           change->trigger_player & trigger_player &&
11612           change->trigger_page & trigger_page_bits &&
11613           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11614       {
11615         change->actual_trigger_element = trigger_element;
11616         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11617         change->actual_trigger_player_bits = trigger_player;
11618         change->actual_trigger_side = trigger_side;
11619         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11620         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11621
11622 #if 0
11623         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11624                element, EL_NAME(element), p);
11625 #endif
11626
11627         if ((change->can_change && !change_done) || change->has_action)
11628         {
11629           int x, y;
11630
11631           SCAN_PLAYFIELD(x, y)
11632           {
11633             if (Feld[x][y] == element)
11634             {
11635               if (change->can_change && !change_done)
11636               {
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638                 /* if element already changed in this frame, not only prevent
11639                    another element change (checked in ChangeElement()), but
11640                    also prevent additional element actions for this element */
11641
11642                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643                     !level.use_action_after_change_bug)
11644                   continue;
11645 #endif
11646
11647 #if 0
11648                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11649                        element, EL_NAME(element), p);
11650 #endif
11651
11652                 ChangeDelay[x][y] = 1;
11653                 ChangeEvent[x][y] = trigger_event;
11654
11655                 HandleElementChange(x, y, p);
11656               }
11657 #if USE_NEW_DELAYED_ACTION
11658               else if (change->has_action)
11659               {
11660 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11661                 /* if element already changed in this frame, not only prevent
11662                    another element change (checked in ChangeElement()), but
11663                    also prevent additional element actions for this element */
11664
11665                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11666                     !level.use_action_after_change_bug)
11667                   continue;
11668 #endif
11669
11670
11671 #if 0
11672                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11673                        element, EL_NAME(element), p);
11674 #endif
11675
11676                 ExecuteCustomElementAction(x, y, element, p);
11677                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11678               }
11679 #else
11680               if (change->has_action)
11681               {
11682                 ExecuteCustomElementAction(x, y, element, p);
11683                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11684               }
11685 #endif
11686             }
11687           }
11688
11689           if (change->can_change)
11690           {
11691             change_done = TRUE;
11692             change_done_any = TRUE;
11693
11694 #if 0
11695             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11696                    element, EL_NAME(element), p);
11697 #endif
11698
11699           }
11700         }
11701       }
11702     }
11703   }
11704
11705   RECURSION_LOOP_DETECTION_END();
11706
11707   return change_done_any;
11708 }
11709
11710 static boolean CheckElementChangeExt(int x, int y,
11711                                      int element,
11712                                      int trigger_element,
11713                                      int trigger_event,
11714                                      int trigger_player,
11715                                      int trigger_side)
11716 {
11717   boolean change_done = FALSE;
11718   int p;
11719
11720   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11721       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11722     return FALSE;
11723
11724   if (Feld[x][y] == EL_BLOCKED)
11725   {
11726     Blocked2Moving(x, y, &x, &y);
11727     element = Feld[x][y];
11728   }
11729
11730 #if 0
11731   /* check if element has already changed */
11732   if (Feld[x][y] != element)
11733     return FALSE;
11734 #else
11735   /* check if element has already changed or is about to change after moving */
11736   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11737        Feld[x][y] != element) ||
11738
11739       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11740        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11741         ChangePage[x][y] != -1)))
11742     return FALSE;
11743 #endif
11744
11745 #if 0
11746   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11747          trigger_event, recursion_loop_depth, recursion_loop_detected,
11748          recursion_loop_element, EL_NAME(recursion_loop_element));
11749 #endif
11750
11751   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11752
11753 #if 0
11754   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11755 #endif
11756
11757   for (p = 0; p < element_info[element].num_change_pages; p++)
11758   {
11759     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11760
11761     /* check trigger element for all events where the element that is checked
11762        for changing interacts with a directly adjacent element -- this is
11763        different to element changes that affect other elements to change on the
11764        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11765     boolean check_trigger_element =
11766       (trigger_event == CE_TOUCHING_X ||
11767        trigger_event == CE_HITTING_X ||
11768        trigger_event == CE_HIT_BY_X ||
11769 #if 1
11770        /* this one was forgotten until 3.2.3 */
11771        trigger_event == CE_DIGGING_X);
11772 #endif
11773
11774     if (change->can_change_or_has_action &&
11775         change->has_event[trigger_event] &&
11776         change->trigger_side & trigger_side &&
11777         change->trigger_player & trigger_player &&
11778         (!check_trigger_element ||
11779          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11780     {
11781       change->actual_trigger_element = trigger_element;
11782       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11783       change->actual_trigger_player_bits = trigger_player;
11784       change->actual_trigger_side = trigger_side;
11785       change->actual_trigger_ce_value = CustomValue[x][y];
11786       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11787
11788       /* special case: trigger element not at (x,y) position for some events */
11789       if (check_trigger_element)
11790       {
11791         static struct
11792         {
11793           int dx, dy;
11794         } move_xy[] =
11795           {
11796             {  0,  0 },
11797             { -1,  0 },
11798             { +1,  0 },
11799             {  0,  0 },
11800             {  0, -1 },
11801             {  0,  0 }, { 0, 0 }, { 0, 0 },
11802             {  0, +1 }
11803           };
11804
11805         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11806         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11807
11808         change->actual_trigger_ce_value = CustomValue[xx][yy];
11809         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11810       }
11811
11812       if (change->can_change && !change_done)
11813       {
11814         ChangeDelay[x][y] = 1;
11815         ChangeEvent[x][y] = trigger_event;
11816
11817         HandleElementChange(x, y, p);
11818
11819         change_done = TRUE;
11820       }
11821 #if USE_NEW_DELAYED_ACTION
11822       else if (change->has_action)
11823       {
11824         ExecuteCustomElementAction(x, y, element, p);
11825         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11826       }
11827 #else
11828       if (change->has_action)
11829       {
11830         ExecuteCustomElementAction(x, y, element, p);
11831         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11832       }
11833 #endif
11834     }
11835   }
11836
11837   RECURSION_LOOP_DETECTION_END();
11838
11839   return change_done;
11840 }
11841
11842 static void PlayPlayerSound(struct PlayerInfo *player)
11843 {
11844   int jx = player->jx, jy = player->jy;
11845   int sound_element = player->artwork_element;
11846   int last_action = player->last_action_waiting;
11847   int action = player->action_waiting;
11848
11849   if (player->is_waiting)
11850   {
11851     if (action != last_action)
11852       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11853     else
11854       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11855   }
11856   else
11857   {
11858     if (action != last_action)
11859       StopSound(element_info[sound_element].sound[last_action]);
11860
11861     if (last_action == ACTION_SLEEPING)
11862       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11863   }
11864 }
11865
11866 static void PlayAllPlayersSound()
11867 {
11868   int i;
11869
11870   for (i = 0; i < MAX_PLAYERS; i++)
11871     if (stored_player[i].active)
11872       PlayPlayerSound(&stored_player[i]);
11873 }
11874
11875 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11876 {
11877   boolean last_waiting = player->is_waiting;
11878   int move_dir = player->MovDir;
11879
11880   player->dir_waiting = move_dir;
11881   player->last_action_waiting = player->action_waiting;
11882
11883   if (is_waiting)
11884   {
11885     if (!last_waiting)          /* not waiting -> waiting */
11886     {
11887       player->is_waiting = TRUE;
11888
11889       player->frame_counter_bored =
11890         FrameCounter +
11891         game.player_boring_delay_fixed +
11892         GetSimpleRandom(game.player_boring_delay_random);
11893       player->frame_counter_sleeping =
11894         FrameCounter +
11895         game.player_sleeping_delay_fixed +
11896         GetSimpleRandom(game.player_sleeping_delay_random);
11897
11898       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11899     }
11900
11901     if (game.player_sleeping_delay_fixed +
11902         game.player_sleeping_delay_random > 0 &&
11903         player->anim_delay_counter == 0 &&
11904         player->post_delay_counter == 0 &&
11905         FrameCounter >= player->frame_counter_sleeping)
11906       player->is_sleeping = TRUE;
11907     else if (game.player_boring_delay_fixed +
11908              game.player_boring_delay_random > 0 &&
11909              FrameCounter >= player->frame_counter_bored)
11910       player->is_bored = TRUE;
11911
11912     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11913                               player->is_bored ? ACTION_BORING :
11914                               ACTION_WAITING);
11915
11916     if (player->is_sleeping && player->use_murphy)
11917     {
11918       /* special case for sleeping Murphy when leaning against non-free tile */
11919
11920       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11921           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11922            !IS_MOVING(player->jx - 1, player->jy)))
11923         move_dir = MV_LEFT;
11924       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11925                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11926                 !IS_MOVING(player->jx + 1, player->jy)))
11927         move_dir = MV_RIGHT;
11928       else
11929         player->is_sleeping = FALSE;
11930
11931       player->dir_waiting = move_dir;
11932     }
11933
11934     if (player->is_sleeping)
11935     {
11936       if (player->num_special_action_sleeping > 0)
11937       {
11938         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11939         {
11940           int last_special_action = player->special_action_sleeping;
11941           int num_special_action = player->num_special_action_sleeping;
11942           int special_action =
11943             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11944              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11945              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11946              last_special_action + 1 : ACTION_SLEEPING);
11947           int special_graphic =
11948             el_act_dir2img(player->artwork_element, special_action, move_dir);
11949
11950           player->anim_delay_counter =
11951             graphic_info[special_graphic].anim_delay_fixed +
11952             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11953           player->post_delay_counter =
11954             graphic_info[special_graphic].post_delay_fixed +
11955             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11956
11957           player->special_action_sleeping = special_action;
11958         }
11959
11960         if (player->anim_delay_counter > 0)
11961         {
11962           player->action_waiting = player->special_action_sleeping;
11963           player->anim_delay_counter--;
11964         }
11965         else if (player->post_delay_counter > 0)
11966         {
11967           player->post_delay_counter--;
11968         }
11969       }
11970     }
11971     else if (player->is_bored)
11972     {
11973       if (player->num_special_action_bored > 0)
11974       {
11975         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11976         {
11977           int special_action =
11978             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11979           int special_graphic =
11980             el_act_dir2img(player->artwork_element, special_action, move_dir);
11981
11982           player->anim_delay_counter =
11983             graphic_info[special_graphic].anim_delay_fixed +
11984             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11985           player->post_delay_counter =
11986             graphic_info[special_graphic].post_delay_fixed +
11987             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11988
11989           player->special_action_bored = special_action;
11990         }
11991
11992         if (player->anim_delay_counter > 0)
11993         {
11994           player->action_waiting = player->special_action_bored;
11995           player->anim_delay_counter--;
11996         }
11997         else if (player->post_delay_counter > 0)
11998         {
11999           player->post_delay_counter--;
12000         }
12001       }
12002     }
12003   }
12004   else if (last_waiting)        /* waiting -> not waiting */
12005   {
12006     player->is_waiting = FALSE;
12007     player->is_bored = FALSE;
12008     player->is_sleeping = FALSE;
12009
12010     player->frame_counter_bored = -1;
12011     player->frame_counter_sleeping = -1;
12012
12013     player->anim_delay_counter = 0;
12014     player->post_delay_counter = 0;
12015
12016     player->dir_waiting = player->MovDir;
12017     player->action_waiting = ACTION_DEFAULT;
12018
12019     player->special_action_bored = ACTION_DEFAULT;
12020     player->special_action_sleeping = ACTION_DEFAULT;
12021   }
12022 }
12023
12024 static void CheckSingleStepMode(struct PlayerInfo *player)
12025 {
12026   if (tape.single_step && tape.recording && !tape.pausing)
12027   {
12028     /* as it is called "single step mode", just return to pause mode when the
12029        player stopped moving after one tile (or never starts moving at all) */
12030     if (!player->is_moving && !player->is_pushing)
12031     {
12032       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12033       SnapField(player, 0, 0);                  /* stop snapping */
12034     }
12035   }
12036 }
12037
12038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12039 {
12040   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12041   int left      = player_action & JOY_LEFT;
12042   int right     = player_action & JOY_RIGHT;
12043   int up        = player_action & JOY_UP;
12044   int down      = player_action & JOY_DOWN;
12045   int button1   = player_action & JOY_BUTTON_1;
12046   int button2   = player_action & JOY_BUTTON_2;
12047   int dx        = (left ? -1 : right ? 1 : 0);
12048   int dy        = (up   ? -1 : down  ? 1 : 0);
12049
12050   if (!player->active || tape.pausing)
12051     return 0;
12052
12053   if (player_action)
12054   {
12055     if (button1)
12056       snapped = SnapField(player, dx, dy);
12057     else
12058     {
12059       if (button2)
12060         dropped = DropElement(player);
12061
12062       moved = MovePlayer(player, dx, dy);
12063     }
12064
12065     CheckSingleStepMode(player);
12066
12067     SetPlayerWaiting(player, FALSE);
12068
12069     return player_action;
12070   }
12071   else
12072   {
12073     /* no actions for this player (no input at player's configured device) */
12074
12075     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12076     SnapField(player, 0, 0);
12077     CheckGravityMovementWhenNotMoving(player);
12078
12079     if (player->MovPos == 0)
12080       SetPlayerWaiting(player, TRUE);
12081
12082     if (player->MovPos == 0)    /* needed for tape.playing */
12083       player->is_moving = FALSE;
12084
12085     player->is_dropping = FALSE;
12086     player->is_dropping_pressed = FALSE;
12087     player->drop_pressed_delay = 0;
12088
12089     CheckSingleStepMode(player);
12090
12091     return 0;
12092   }
12093 }
12094
12095 static void CheckLevelTime()
12096 {
12097   int i;
12098
12099   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12100   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12101   {
12102     if (level.native_em_level->lev->home == 0)  /* all players at home */
12103     {
12104       PlayerWins(local_player);
12105
12106       AllPlayersGone = TRUE;
12107
12108       level.native_em_level->lev->home = -1;
12109     }
12110
12111     if (level.native_em_level->ply[0]->alive == 0 &&
12112         level.native_em_level->ply[1]->alive == 0 &&
12113         level.native_em_level->ply[2]->alive == 0 &&
12114         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12115       AllPlayersGone = TRUE;
12116   }
12117   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12118   {
12119     if (game_sp.LevelSolved &&
12120         !game_sp.GameOver)                              /* game won */
12121     {
12122       PlayerWins(local_player);
12123
12124       game_sp.GameOver = TRUE;
12125
12126       AllPlayersGone = TRUE;
12127     }
12128
12129     if (game_sp.GameOver)                               /* game lost */
12130       AllPlayersGone = TRUE;
12131   }
12132
12133   if (TimeFrames >= FRAMES_PER_SECOND)
12134   {
12135     TimeFrames = 0;
12136     TapeTime++;
12137
12138     for (i = 0; i < MAX_PLAYERS; i++)
12139     {
12140       struct PlayerInfo *player = &stored_player[i];
12141
12142       if (SHIELD_ON(player))
12143       {
12144         player->shield_normal_time_left--;
12145
12146         if (player->shield_deadly_time_left > 0)
12147           player->shield_deadly_time_left--;
12148       }
12149     }
12150
12151     if (!local_player->LevelSolved && !level.use_step_counter)
12152     {
12153       TimePlayed++;
12154
12155       if (TimeLeft > 0)
12156       {
12157         TimeLeft--;
12158
12159         if (TimeLeft <= 10 && setup.time_limit)
12160           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12161
12162 #if 1
12163         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12164
12165         DisplayGameControlValues();
12166 #else
12167         DrawGameValue_Time(TimeLeft);
12168 #endif
12169
12170         if (!TimeLeft && setup.time_limit)
12171         {
12172           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12173             level.native_em_level->lev->killed_out_of_time = TRUE;
12174           else
12175             for (i = 0; i < MAX_PLAYERS; i++)
12176               KillPlayer(&stored_player[i]);
12177         }
12178       }
12179 #if 1
12180       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12181       {
12182         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12183
12184         DisplayGameControlValues();
12185       }
12186 #else
12187       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12188         DrawGameValue_Time(TimePlayed);
12189 #endif
12190
12191       level.native_em_level->lev->time =
12192         (level.time == 0 ? TimePlayed : TimeLeft);
12193     }
12194
12195     if (tape.recording || tape.playing)
12196       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12197   }
12198
12199 #if 1
12200   UpdateAndDisplayGameControlValues();
12201 #else
12202   UpdateGameDoorValues();
12203   DrawGameDoorValues();
12204 #endif
12205 }
12206
12207 void AdvanceFrameAndPlayerCounters(int player_nr)
12208 {
12209   int i;
12210
12211   /* advance frame counters (global frame counter and time frame counter) */
12212   FrameCounter++;
12213   TimeFrames++;
12214
12215   /* advance player counters (counters for move delay, move animation etc.) */
12216   for (i = 0; i < MAX_PLAYERS; i++)
12217   {
12218     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12219     int move_delay_value = stored_player[i].move_delay_value;
12220     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12221
12222     if (!advance_player_counters)       /* not all players may be affected */
12223       continue;
12224
12225 #if USE_NEW_PLAYER_ANIM
12226     if (move_frames == 0)       /* less than one move per game frame */
12227     {
12228       int stepsize = TILEX / move_delay_value;
12229       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12230       int count = (stored_player[i].is_moving ?
12231                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12232
12233       if (count % delay == 0)
12234         move_frames = 1;
12235     }
12236 #endif
12237
12238     stored_player[i].Frame += move_frames;
12239
12240     if (stored_player[i].MovPos != 0)
12241       stored_player[i].StepFrame += move_frames;
12242
12243     if (stored_player[i].move_delay > 0)
12244       stored_player[i].move_delay--;
12245
12246     /* due to bugs in previous versions, counter must count up, not down */
12247     if (stored_player[i].push_delay != -1)
12248       stored_player[i].push_delay++;
12249
12250     if (stored_player[i].drop_delay > 0)
12251       stored_player[i].drop_delay--;
12252
12253     if (stored_player[i].is_dropping_pressed)
12254       stored_player[i].drop_pressed_delay++;
12255   }
12256 }
12257
12258 void StartGameActions(boolean init_network_game, boolean record_tape,
12259                       long random_seed)
12260 {
12261   unsigned long new_random_seed = InitRND(random_seed);
12262
12263   if (record_tape)
12264     TapeStartRecording(new_random_seed);
12265
12266 #if defined(NETWORK_AVALIABLE)
12267   if (init_network_game)
12268   {
12269     SendToServer_StartPlaying();
12270
12271     return;
12272   }
12273 #endif
12274
12275   InitGame();
12276 }
12277
12278 void GameActions()
12279 {
12280   static unsigned long game_frame_delay = 0;
12281   unsigned long game_frame_delay_value;
12282   byte *recorded_player_action;
12283   byte summarized_player_action = 0;
12284   byte tape_action[MAX_PLAYERS];
12285   int i;
12286
12287   /* detect endless loops, caused by custom element programming */
12288   if (recursion_loop_detected && recursion_loop_depth == 0)
12289   {
12290     char *message = getStringCat3("Internal Error ! Element ",
12291                                   EL_NAME(recursion_loop_element),
12292                                   " caused endless loop ! Quit the game ?");
12293
12294     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12295           EL_NAME(recursion_loop_element));
12296
12297     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12298
12299     recursion_loop_detected = FALSE;    /* if game should be continued */
12300
12301     free(message);
12302
12303     return;
12304   }
12305
12306   if (game.restart_level)
12307     StartGameActions(options.network, setup.autorecord, level.random_seed);
12308
12309   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12310   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12311   {
12312     if (level.native_em_level->lev->home == 0)  /* all players at home */
12313     {
12314       PlayerWins(local_player);
12315
12316       AllPlayersGone = TRUE;
12317
12318       level.native_em_level->lev->home = -1;
12319     }
12320
12321     if (level.native_em_level->ply[0]->alive == 0 &&
12322         level.native_em_level->ply[1]->alive == 0 &&
12323         level.native_em_level->ply[2]->alive == 0 &&
12324         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12325       AllPlayersGone = TRUE;
12326   }
12327   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12328   {
12329     if (game_sp.LevelSolved &&
12330         !game_sp.GameOver)                              /* game won */
12331     {
12332       PlayerWins(local_player);
12333
12334       game_sp.GameOver = TRUE;
12335
12336       AllPlayersGone = TRUE;
12337     }
12338
12339     if (game_sp.GameOver)                               /* game lost */
12340       AllPlayersGone = TRUE;
12341   }
12342
12343   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12344     GameWon();
12345
12346   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12347     TapeStop();
12348
12349   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12350     return;
12351
12352   game_frame_delay_value =
12353     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12354
12355   if (tape.playing && tape.warp_forward && !tape.pausing)
12356     game_frame_delay_value = 0;
12357
12358   /* ---------- main game synchronization point ---------- */
12359
12360   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12361
12362   if (network_playing && !network_player_action_received)
12363   {
12364     /* try to get network player actions in time */
12365
12366 #if defined(NETWORK_AVALIABLE)
12367     /* last chance to get network player actions without main loop delay */
12368     HandleNetworking();
12369 #endif
12370
12371     /* game was quit by network peer */
12372     if (game_status != GAME_MODE_PLAYING)
12373       return;
12374
12375     if (!network_player_action_received)
12376       return;           /* failed to get network player actions in time */
12377
12378     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12379   }
12380
12381   if (tape.pausing)
12382     return;
12383
12384   /* at this point we know that we really continue executing the game */
12385
12386   network_player_action_received = FALSE;
12387
12388   /* when playing tape, read previously recorded player input from tape data */
12389   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12390
12391 #if 1
12392   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12393   if (tape.pausing)
12394     return;
12395 #endif
12396
12397   if (tape.set_centered_player)
12398   {
12399     game.centered_player_nr_next = tape.centered_player_nr_next;
12400     game.set_centered_player = TRUE;
12401   }
12402
12403   for (i = 0; i < MAX_PLAYERS; i++)
12404   {
12405     summarized_player_action |= stored_player[i].action;
12406
12407     if (!network_playing)
12408       stored_player[i].effective_action = stored_player[i].action;
12409   }
12410
12411 #if defined(NETWORK_AVALIABLE)
12412   if (network_playing)
12413     SendToServer_MovePlayer(summarized_player_action);
12414 #endif
12415
12416   if (!options.network && !setup.team_mode)
12417     local_player->effective_action = summarized_player_action;
12418
12419   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12420   {
12421     for (i = 0; i < MAX_PLAYERS; i++)
12422       stored_player[i].effective_action =
12423         (i == game.centered_player_nr ? summarized_player_action : 0);
12424   }
12425
12426   if (recorded_player_action != NULL)
12427     for (i = 0; i < MAX_PLAYERS; i++)
12428       stored_player[i].effective_action = recorded_player_action[i];
12429
12430   for (i = 0; i < MAX_PLAYERS; i++)
12431   {
12432     tape_action[i] = stored_player[i].effective_action;
12433
12434     /* (this can only happen in the R'n'D game engine) */
12435     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12436       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12437   }
12438
12439   /* only record actions from input devices, but not programmed actions */
12440   if (tape.recording)
12441     TapeRecordAction(tape_action);
12442
12443 #if USE_NEW_PLAYER_ASSIGNMENTS
12444   {
12445     byte mapped_action[MAX_PLAYERS];
12446
12447     for (i = 0; i < MAX_PLAYERS; i++)
12448       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12449
12450     for (i = 0; i < MAX_PLAYERS; i++)
12451       stored_player[i].effective_action = mapped_action[i];
12452   }
12453 #endif
12454
12455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12456   {
12457     GameActions_EM_Main();
12458   }
12459   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12460   {
12461     GameActions_SP_Main();
12462   }
12463   else
12464   {
12465     GameActions_RND();
12466   }
12467 }
12468
12469 void GameActions_EM_Main()
12470 {
12471   byte effective_action[MAX_PLAYERS];
12472   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12473   int i;
12474
12475   for (i = 0; i < MAX_PLAYERS; i++)
12476     effective_action[i] = stored_player[i].effective_action;
12477
12478   GameActions_EM(effective_action, warp_mode);
12479
12480   CheckLevelTime();
12481
12482   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12483 }
12484
12485 void GameActions_SP_Main()
12486 {
12487   byte effective_action[MAX_PLAYERS];
12488   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12489   int i;
12490
12491   for (i = 0; i < MAX_PLAYERS; i++)
12492     effective_action[i] = stored_player[i].effective_action;
12493
12494   GameActions_SP(effective_action, warp_mode);
12495
12496   CheckLevelTime();
12497
12498   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12499 }
12500
12501 void GameActions_RND()
12502 {
12503   int magic_wall_x = 0, magic_wall_y = 0;
12504   int i, x, y, element, graphic;
12505
12506   InitPlayfieldScanModeVars();
12507
12508 #if USE_ONE_MORE_CHANGE_PER_FRAME
12509   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12510   {
12511     SCAN_PLAYFIELD(x, y)
12512     {
12513       ChangeCount[x][y] = 0;
12514       ChangeEvent[x][y] = -1;
12515     }
12516   }
12517 #endif
12518
12519   if (game.set_centered_player)
12520   {
12521     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12522
12523     /* switching to "all players" only possible if all players fit to screen */
12524     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12525     {
12526       game.centered_player_nr_next = game.centered_player_nr;
12527       game.set_centered_player = FALSE;
12528     }
12529
12530     /* do not switch focus to non-existing (or non-active) player */
12531     if (game.centered_player_nr_next >= 0 &&
12532         !stored_player[game.centered_player_nr_next].active)
12533     {
12534       game.centered_player_nr_next = game.centered_player_nr;
12535       game.set_centered_player = FALSE;
12536     }
12537   }
12538
12539   if (game.set_centered_player &&
12540       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12541   {
12542     int sx, sy;
12543
12544     if (game.centered_player_nr_next == -1)
12545     {
12546       setScreenCenteredToAllPlayers(&sx, &sy);
12547     }
12548     else
12549     {
12550       sx = stored_player[game.centered_player_nr_next].jx;
12551       sy = stored_player[game.centered_player_nr_next].jy;
12552     }
12553
12554     game.centered_player_nr = game.centered_player_nr_next;
12555     game.set_centered_player = FALSE;
12556
12557     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12558     DrawGameDoorValues();
12559   }
12560
12561   for (i = 0; i < MAX_PLAYERS; i++)
12562   {
12563     int actual_player_action = stored_player[i].effective_action;
12564
12565 #if 1
12566     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12567        - rnd_equinox_tetrachloride 048
12568        - rnd_equinox_tetrachloride_ii 096
12569        - rnd_emanuel_schmieg 002
12570        - doctor_sloan_ww 001, 020
12571     */
12572     if (stored_player[i].MovPos == 0)
12573       CheckGravityMovement(&stored_player[i]);
12574 #endif
12575
12576     /* overwrite programmed action with tape action */
12577     if (stored_player[i].programmed_action)
12578       actual_player_action = stored_player[i].programmed_action;
12579
12580     PlayerActions(&stored_player[i], actual_player_action);
12581
12582     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12583   }
12584
12585   ScrollScreen(NULL, SCROLL_GO_ON);
12586
12587   /* for backwards compatibility, the following code emulates a fixed bug that
12588      occured when pushing elements (causing elements that just made their last
12589      pushing step to already (if possible) make their first falling step in the
12590      same game frame, which is bad); this code is also needed to use the famous
12591      "spring push bug" which is used in older levels and might be wanted to be
12592      used also in newer levels, but in this case the buggy pushing code is only
12593      affecting the "spring" element and no other elements */
12594
12595   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12596   {
12597     for (i = 0; i < MAX_PLAYERS; i++)
12598     {
12599       struct PlayerInfo *player = &stored_player[i];
12600       int x = player->jx;
12601       int y = player->jy;
12602
12603       if (player->active && player->is_pushing && player->is_moving &&
12604           IS_MOVING(x, y) &&
12605           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12606            Feld[x][y] == EL_SPRING))
12607       {
12608         ContinueMoving(x, y);
12609
12610         /* continue moving after pushing (this is actually a bug) */
12611         if (!IS_MOVING(x, y))
12612           Stop[x][y] = FALSE;
12613       }
12614     }
12615   }
12616
12617 #if 0
12618   debug_print_timestamp(0, "start main loop profiling");
12619 #endif
12620
12621   SCAN_PLAYFIELD(x, y)
12622   {
12623     ChangeCount[x][y] = 0;
12624     ChangeEvent[x][y] = -1;
12625
12626     /* this must be handled before main playfield loop */
12627     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12628     {
12629       MovDelay[x][y]--;
12630       if (MovDelay[x][y] <= 0)
12631         RemoveField(x, y);
12632     }
12633
12634 #if USE_NEW_SNAP_DELAY
12635     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12636     {
12637       MovDelay[x][y]--;
12638       if (MovDelay[x][y] <= 0)
12639       {
12640         RemoveField(x, y);
12641         TEST_DrawLevelField(x, y);
12642
12643         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12644       }
12645     }
12646 #endif
12647
12648 #if DEBUG
12649     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12650     {
12651       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12652       printf("GameActions(): This should never happen!\n");
12653
12654       ChangePage[x][y] = -1;
12655     }
12656 #endif
12657
12658     Stop[x][y] = FALSE;
12659     if (WasJustMoving[x][y] > 0)
12660       WasJustMoving[x][y]--;
12661     if (WasJustFalling[x][y] > 0)
12662       WasJustFalling[x][y]--;
12663     if (CheckCollision[x][y] > 0)
12664       CheckCollision[x][y]--;
12665     if (CheckImpact[x][y] > 0)
12666       CheckImpact[x][y]--;
12667
12668     GfxFrame[x][y]++;
12669
12670     /* reset finished pushing action (not done in ContinueMoving() to allow
12671        continuous pushing animation for elements with zero push delay) */
12672     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12673     {
12674       ResetGfxAnimation(x, y);
12675       TEST_DrawLevelField(x, y);
12676     }
12677
12678 #if DEBUG
12679     if (IS_BLOCKED(x, y))
12680     {
12681       int oldx, oldy;
12682
12683       Blocked2Moving(x, y, &oldx, &oldy);
12684       if (!IS_MOVING(oldx, oldy))
12685       {
12686         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12687         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12688         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12689         printf("GameActions(): This should never happen!\n");
12690       }
12691     }
12692 #endif
12693   }
12694
12695 #if 0
12696   debug_print_timestamp(0, "- time for pre-main loop:");
12697 #endif
12698
12699 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12700   SCAN_PLAYFIELD(x, y)
12701   {
12702     element = Feld[x][y];
12703     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12704
12705 #if 1
12706     {
12707 #if 1
12708       int element2 = element;
12709       int graphic2 = graphic;
12710 #else
12711       int element2 = Feld[x][y];
12712       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12713 #endif
12714       int last_gfx_frame = GfxFrame[x][y];
12715
12716       if (graphic_info[graphic2].anim_global_sync)
12717         GfxFrame[x][y] = FrameCounter;
12718       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12719         GfxFrame[x][y] = CustomValue[x][y];
12720       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12721         GfxFrame[x][y] = element_info[element2].collect_score;
12722       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12723         GfxFrame[x][y] = ChangeDelay[x][y];
12724
12725       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12726         DrawLevelGraphicAnimation(x, y, graphic2);
12727     }
12728 #else
12729     ResetGfxFrame(x, y, TRUE);
12730 #endif
12731
12732 #if 1
12733     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12734         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12735       ResetRandomAnimationValue(x, y);
12736 #endif
12737
12738 #if 1
12739     SetRandomAnimationValue(x, y);
12740 #endif
12741
12742 #if 1
12743     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12744 #endif
12745   }
12746 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12747
12748 #if 0
12749   debug_print_timestamp(0, "- time for TEST loop:     -->");
12750 #endif
12751
12752   SCAN_PLAYFIELD(x, y)
12753   {
12754     element = Feld[x][y];
12755     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12756
12757     ResetGfxFrame(x, y, TRUE);
12758
12759     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12760         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12761       ResetRandomAnimationValue(x, y);
12762
12763     SetRandomAnimationValue(x, y);
12764
12765     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12766
12767     if (IS_INACTIVE(element))
12768     {
12769       if (IS_ANIMATED(graphic))
12770         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12771
12772       continue;
12773     }
12774
12775     /* this may take place after moving, so 'element' may have changed */
12776     if (IS_CHANGING(x, y) &&
12777         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12778     {
12779       int page = element_info[element].event_page_nr[CE_DELAY];
12780
12781 #if 1
12782       HandleElementChange(x, y, page);
12783 #else
12784       if (CAN_CHANGE(element))
12785         HandleElementChange(x, y, page);
12786
12787       if (HAS_ACTION(element))
12788         ExecuteCustomElementAction(x, y, element, page);
12789 #endif
12790
12791       element = Feld[x][y];
12792       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12793     }
12794
12795 #if 0   // ---------------------------------------------------------------------
12796
12797     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12798     {
12799       StartMoving(x, y);
12800
12801       element = Feld[x][y];
12802       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12803
12804       if (IS_ANIMATED(graphic) &&
12805           !IS_MOVING(x, y) &&
12806           !Stop[x][y])
12807         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12808
12809       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12810         TEST_DrawTwinkleOnField(x, y);
12811     }
12812     else if (IS_MOVING(x, y))
12813       ContinueMoving(x, y);
12814     else
12815     {
12816       switch (element)
12817       {
12818         case EL_ACID:
12819         case EL_EXIT_OPEN:
12820         case EL_EM_EXIT_OPEN:
12821         case EL_SP_EXIT_OPEN:
12822         case EL_STEEL_EXIT_OPEN:
12823         case EL_EM_STEEL_EXIT_OPEN:
12824         case EL_SP_TERMINAL:
12825         case EL_SP_TERMINAL_ACTIVE:
12826         case EL_EXTRA_TIME:
12827         case EL_SHIELD_NORMAL:
12828         case EL_SHIELD_DEADLY:
12829           if (IS_ANIMATED(graphic))
12830             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12831           break;
12832
12833         case EL_DYNAMITE_ACTIVE:
12834         case EL_EM_DYNAMITE_ACTIVE:
12835         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12836         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12837         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12838         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12839         case EL_SP_DISK_RED_ACTIVE:
12840           CheckDynamite(x, y);
12841           break;
12842
12843         case EL_AMOEBA_GROWING:
12844           AmoebeWaechst(x, y);
12845           break;
12846
12847         case EL_AMOEBA_SHRINKING:
12848           AmoebaDisappearing(x, y);
12849           break;
12850
12851 #if !USE_NEW_AMOEBA_CODE
12852         case EL_AMOEBA_WET:
12853         case EL_AMOEBA_DRY:
12854         case EL_AMOEBA_FULL:
12855         case EL_BD_AMOEBA:
12856         case EL_EMC_DRIPPER:
12857           AmoebeAbleger(x, y);
12858           break;
12859 #endif
12860
12861         case EL_GAME_OF_LIFE:
12862         case EL_BIOMAZE:
12863           Life(x, y);
12864           break;
12865
12866         case EL_EXIT_CLOSED:
12867           CheckExit(x, y);
12868           break;
12869
12870         case EL_EM_EXIT_CLOSED:
12871           CheckExitEM(x, y);
12872           break;
12873
12874         case EL_STEEL_EXIT_CLOSED:
12875           CheckExitSteel(x, y);
12876           break;
12877
12878         case EL_EM_STEEL_EXIT_CLOSED:
12879           CheckExitSteelEM(x, y);
12880           break;
12881
12882         case EL_SP_EXIT_CLOSED:
12883           CheckExitSP(x, y);
12884           break;
12885
12886         case EL_EXPANDABLE_WALL_GROWING:
12887         case EL_EXPANDABLE_STEELWALL_GROWING:
12888           MauerWaechst(x, y);
12889           break;
12890
12891         case EL_EXPANDABLE_WALL:
12892         case EL_EXPANDABLE_WALL_HORIZONTAL:
12893         case EL_EXPANDABLE_WALL_VERTICAL:
12894         case EL_EXPANDABLE_WALL_ANY:
12895         case EL_BD_EXPANDABLE_WALL:
12896           MauerAbleger(x, y);
12897           break;
12898
12899         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12900         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12901         case EL_EXPANDABLE_STEELWALL_ANY:
12902           MauerAblegerStahl(x, y);
12903           break;
12904
12905         case EL_FLAMES:
12906           CheckForDragon(x, y);
12907           break;
12908
12909         case EL_EXPLOSION:
12910           break;
12911
12912         case EL_ELEMENT_SNAPPING:
12913         case EL_DIAGONAL_SHRINKING:
12914         case EL_DIAGONAL_GROWING:
12915         {
12916           graphic =
12917             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12918
12919           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920           break;
12921         }
12922
12923         default:
12924           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12925             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12926           break;
12927       }
12928     }
12929
12930 #else   // ---------------------------------------------------------------------
12931
12932     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12933     {
12934       StartMoving(x, y);
12935
12936       element = Feld[x][y];
12937       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12938
12939       if (IS_ANIMATED(graphic) &&
12940           !IS_MOVING(x, y) &&
12941           !Stop[x][y])
12942         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12943
12944       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12945         TEST_DrawTwinkleOnField(x, y);
12946     }
12947     else if ((element == EL_ACID ||
12948               element == EL_EXIT_OPEN ||
12949               element == EL_EM_EXIT_OPEN ||
12950               element == EL_SP_EXIT_OPEN ||
12951               element == EL_STEEL_EXIT_OPEN ||
12952               element == EL_EM_STEEL_EXIT_OPEN ||
12953               element == EL_SP_TERMINAL ||
12954               element == EL_SP_TERMINAL_ACTIVE ||
12955               element == EL_EXTRA_TIME ||
12956               element == EL_SHIELD_NORMAL ||
12957               element == EL_SHIELD_DEADLY) &&
12958              IS_ANIMATED(graphic))
12959       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12960     else if (IS_MOVING(x, y))
12961       ContinueMoving(x, y);
12962     else if (IS_ACTIVE_BOMB(element))
12963       CheckDynamite(x, y);
12964     else if (element == EL_AMOEBA_GROWING)
12965       AmoebeWaechst(x, y);
12966     else if (element == EL_AMOEBA_SHRINKING)
12967       AmoebaDisappearing(x, y);
12968
12969 #if !USE_NEW_AMOEBA_CODE
12970     else if (IS_AMOEBALIVE(element))
12971       AmoebeAbleger(x, y);
12972 #endif
12973
12974     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12975       Life(x, y);
12976     else if (element == EL_EXIT_CLOSED)
12977       CheckExit(x, y);
12978     else if (element == EL_EM_EXIT_CLOSED)
12979       CheckExitEM(x, y);
12980     else if (element == EL_STEEL_EXIT_CLOSED)
12981       CheckExitSteel(x, y);
12982     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12983       CheckExitSteelEM(x, y);
12984     else if (element == EL_SP_EXIT_CLOSED)
12985       CheckExitSP(x, y);
12986     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12987              element == EL_EXPANDABLE_STEELWALL_GROWING)
12988       MauerWaechst(x, y);
12989     else if (element == EL_EXPANDABLE_WALL ||
12990              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12991              element == EL_EXPANDABLE_WALL_VERTICAL ||
12992              element == EL_EXPANDABLE_WALL_ANY ||
12993              element == EL_BD_EXPANDABLE_WALL)
12994       MauerAbleger(x, y);
12995     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12996              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12997              element == EL_EXPANDABLE_STEELWALL_ANY)
12998       MauerAblegerStahl(x, y);
12999     else if (element == EL_FLAMES)
13000       CheckForDragon(x, y);
13001     else if (element == EL_EXPLOSION)
13002       ; /* drawing of correct explosion animation is handled separately */
13003     else if (element == EL_ELEMENT_SNAPPING ||
13004              element == EL_DIAGONAL_SHRINKING ||
13005              element == EL_DIAGONAL_GROWING)
13006     {
13007       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13008
13009       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13010     }
13011     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13012       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13013
13014 #endif  // ---------------------------------------------------------------------
13015
13016     if (IS_BELT_ACTIVE(element))
13017       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13018
13019     if (game.magic_wall_active)
13020     {
13021       int jx = local_player->jx, jy = local_player->jy;
13022
13023       /* play the element sound at the position nearest to the player */
13024       if ((element == EL_MAGIC_WALL_FULL ||
13025            element == EL_MAGIC_WALL_ACTIVE ||
13026            element == EL_MAGIC_WALL_EMPTYING ||
13027            element == EL_BD_MAGIC_WALL_FULL ||
13028            element == EL_BD_MAGIC_WALL_ACTIVE ||
13029            element == EL_BD_MAGIC_WALL_EMPTYING ||
13030            element == EL_DC_MAGIC_WALL_FULL ||
13031            element == EL_DC_MAGIC_WALL_ACTIVE ||
13032            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13033           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13034       {
13035         magic_wall_x = x;
13036         magic_wall_y = y;
13037       }
13038     }
13039   }
13040
13041 #if 0
13042   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13043 #endif
13044
13045 #if USE_NEW_AMOEBA_CODE
13046   /* new experimental amoeba growth stuff */
13047   if (!(FrameCounter % 8))
13048   {
13049     static unsigned long random = 1684108901;
13050
13051     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13052     {
13053       x = RND(lev_fieldx);
13054       y = RND(lev_fieldy);
13055       element = Feld[x][y];
13056
13057       if (!IS_PLAYER(x,y) &&
13058           (element == EL_EMPTY ||
13059            CAN_GROW_INTO(element) ||
13060            element == EL_QUICKSAND_EMPTY ||
13061            element == EL_QUICKSAND_FAST_EMPTY ||
13062            element == EL_ACID_SPLASH_LEFT ||
13063            element == EL_ACID_SPLASH_RIGHT))
13064       {
13065         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13066             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13067             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13068             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13069           Feld[x][y] = EL_AMOEBA_DROP;
13070       }
13071
13072       random = random * 129 + 1;
13073     }
13074   }
13075 #endif
13076
13077 #if 0
13078   if (game.explosions_delayed)
13079 #endif
13080   {
13081     game.explosions_delayed = FALSE;
13082
13083     SCAN_PLAYFIELD(x, y)
13084     {
13085       element = Feld[x][y];
13086
13087       if (ExplodeField[x][y])
13088         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13089       else if (element == EL_EXPLOSION)
13090         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13091
13092       ExplodeField[x][y] = EX_TYPE_NONE;
13093     }
13094
13095     game.explosions_delayed = TRUE;
13096   }
13097
13098   if (game.magic_wall_active)
13099   {
13100     if (!(game.magic_wall_time_left % 4))
13101     {
13102       int element = Feld[magic_wall_x][magic_wall_y];
13103
13104       if (element == EL_BD_MAGIC_WALL_FULL ||
13105           element == EL_BD_MAGIC_WALL_ACTIVE ||
13106           element == EL_BD_MAGIC_WALL_EMPTYING)
13107         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13108       else if (element == EL_DC_MAGIC_WALL_FULL ||
13109                element == EL_DC_MAGIC_WALL_ACTIVE ||
13110                element == EL_DC_MAGIC_WALL_EMPTYING)
13111         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13112       else
13113         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13114     }
13115
13116     if (game.magic_wall_time_left > 0)
13117     {
13118       game.magic_wall_time_left--;
13119
13120       if (!game.magic_wall_time_left)
13121       {
13122         SCAN_PLAYFIELD(x, y)
13123         {
13124           element = Feld[x][y];
13125
13126           if (element == EL_MAGIC_WALL_ACTIVE ||
13127               element == EL_MAGIC_WALL_FULL)
13128           {
13129             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13130             TEST_DrawLevelField(x, y);
13131           }
13132           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13133                    element == EL_BD_MAGIC_WALL_FULL)
13134           {
13135             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13136             TEST_DrawLevelField(x, y);
13137           }
13138           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13139                    element == EL_DC_MAGIC_WALL_FULL)
13140           {
13141             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13142             TEST_DrawLevelField(x, y);
13143           }
13144         }
13145
13146         game.magic_wall_active = FALSE;
13147       }
13148     }
13149   }
13150
13151   if (game.light_time_left > 0)
13152   {
13153     game.light_time_left--;
13154
13155     if (game.light_time_left == 0)
13156       RedrawAllLightSwitchesAndInvisibleElements();
13157   }
13158
13159   if (game.timegate_time_left > 0)
13160   {
13161     game.timegate_time_left--;
13162
13163     if (game.timegate_time_left == 0)
13164       CloseAllOpenTimegates();
13165   }
13166
13167   if (game.lenses_time_left > 0)
13168   {
13169     game.lenses_time_left--;
13170
13171     if (game.lenses_time_left == 0)
13172       RedrawAllInvisibleElementsForLenses();
13173   }
13174
13175   if (game.magnify_time_left > 0)
13176   {
13177     game.magnify_time_left--;
13178
13179     if (game.magnify_time_left == 0)
13180       RedrawAllInvisibleElementsForMagnifier();
13181   }
13182
13183   for (i = 0; i < MAX_PLAYERS; i++)
13184   {
13185     struct PlayerInfo *player = &stored_player[i];
13186
13187     if (SHIELD_ON(player))
13188     {
13189       if (player->shield_deadly_time_left)
13190         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13191       else if (player->shield_normal_time_left)
13192         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13193     }
13194   }
13195
13196 #if USE_DELAYED_GFX_REDRAW
13197   SCAN_PLAYFIELD(x, y)
13198   {
13199 #if 1
13200     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13201 #else
13202     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13203         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13204 #endif
13205     {
13206       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13207          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13208
13209       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13210         DrawLevelField(x, y);
13211
13212       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13213         DrawLevelFieldCrumbled(x, y);
13214
13215       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13216         DrawLevelFieldCrumbledNeighbours(x, y);
13217
13218       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13219         DrawTwinkleOnField(x, y);
13220     }
13221
13222     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13223   }
13224 #endif
13225
13226   CheckLevelTime();
13227
13228   DrawAllPlayers();
13229   PlayAllPlayersSound();
13230
13231   if (options.debug)                    /* calculate frames per second */
13232   {
13233     static unsigned long fps_counter = 0;
13234     static int fps_frames = 0;
13235     unsigned long fps_delay_ms = Counter() - fps_counter;
13236
13237     fps_frames++;
13238
13239     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13240     {
13241       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13242
13243       fps_frames = 0;
13244       fps_counter = Counter();
13245     }
13246
13247     redraw_mask |= REDRAW_FPS;
13248   }
13249
13250   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13251
13252   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13253   {
13254     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13255
13256     local_player->show_envelope = 0;
13257   }
13258
13259 #if 0
13260   debug_print_timestamp(0, "stop main loop profiling ");
13261   printf("----------------------------------------------------------\n");
13262 #endif
13263
13264   /* use random number generator in every frame to make it less predictable */
13265   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13266     RND(1);
13267 }
13268
13269 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13270 {
13271   int min_x = x, min_y = y, max_x = x, max_y = y;
13272   int i;
13273
13274   for (i = 0; i < MAX_PLAYERS; i++)
13275   {
13276     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13277
13278     if (!stored_player[i].active || &stored_player[i] == player)
13279       continue;
13280
13281     min_x = MIN(min_x, jx);
13282     min_y = MIN(min_y, jy);
13283     max_x = MAX(max_x, jx);
13284     max_y = MAX(max_y, jy);
13285   }
13286
13287   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13288 }
13289
13290 static boolean AllPlayersInVisibleScreen()
13291 {
13292   int i;
13293
13294   for (i = 0; i < MAX_PLAYERS; i++)
13295   {
13296     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13297
13298     if (!stored_player[i].active)
13299       continue;
13300
13301     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13302       return FALSE;
13303   }
13304
13305   return TRUE;
13306 }
13307
13308 void ScrollLevel(int dx, int dy)
13309 {
13310 #if 0
13311   /* (directly solved in BlitBitmap() now) */
13312   static Bitmap *bitmap_db_field2 = NULL;
13313   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13314   int x, y;
13315 #else
13316   int x, y;
13317 #endif
13318
13319 #if 0
13320   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13321   /* only horizontal XOR vertical scroll direction allowed */
13322   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13323     return;
13324 #endif
13325
13326 #if 0
13327   /* (directly solved in BlitBitmap() now) */
13328   if (bitmap_db_field2 == NULL)
13329     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13330
13331   /* needed when blitting directly to same bitmap -- should not be needed with
13332      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13333   BlitBitmap(drawto_field, bitmap_db_field2,
13334              FX + TILEX * (dx == -1) - softscroll_offset,
13335              FY + TILEY * (dy == -1) - softscroll_offset,
13336              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13337              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13338              FX + TILEX * (dx == 1) - softscroll_offset,
13339              FY + TILEY * (dy == 1) - softscroll_offset);
13340   BlitBitmap(bitmap_db_field2, drawto_field,
13341              FX + TILEX * (dx == 1) - softscroll_offset,
13342              FY + TILEY * (dy == 1) - softscroll_offset,
13343              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13344              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13345              FX + TILEX * (dx == 1) - softscroll_offset,
13346              FY + TILEY * (dy == 1) - softscroll_offset);
13347
13348 #else
13349
13350 #if 0
13351   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13352   int xsize = (BX2 - BX1 + 1);
13353   int ysize = (BY2 - BY1 + 1);
13354   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13355   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13356   int step  = (start < end ? +1 : -1);
13357
13358   for (i = start; i != end; i += step)
13359   {
13360     BlitBitmap(drawto_field, drawto_field,
13361                FX + TILEX * (dx != 0 ? i + step : 0),
13362                FY + TILEY * (dy != 0 ? i + step : 0),
13363                TILEX * (dx != 0 ? 1 : xsize),
13364                TILEY * (dy != 0 ? 1 : ysize),
13365                FX + TILEX * (dx != 0 ? i : 0),
13366                FY + TILEY * (dy != 0 ? i : 0));
13367   }
13368
13369 #else
13370
13371 #if NEW_TILESIZE
13372 #if NEW_SCROLL
13373   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13374 #else
13375   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13376 #endif
13377 #else
13378 #if NEW_SCROLL
13379   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13380 #else
13381   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13382 #endif
13383 #endif
13384
13385 #if NEW_TILESIZE
13386   BlitBitmap(drawto_field, drawto_field,
13387              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13388              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13389              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13390              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13391              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13392              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13393 #else
13394   BlitBitmap(drawto_field, drawto_field,
13395              FX + TILEX * (dx == -1) - softscroll_offset,
13396              FY + TILEY * (dy == -1) - softscroll_offset,
13397              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13398              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13399              FX + TILEX * (dx == 1) - softscroll_offset,
13400              FY + TILEY * (dy == 1) - softscroll_offset);
13401 #endif
13402
13403 #endif
13404 #endif
13405
13406   if (dx != 0)
13407   {
13408     x = (dx == 1 ? BX1 : BX2);
13409     for (y = BY1; y <= BY2; y++)
13410       DrawScreenField(x, y);
13411   }
13412
13413   if (dy != 0)
13414   {
13415     y = (dy == 1 ? BY1 : BY2);
13416     for (x = BX1; x <= BX2; x++)
13417       DrawScreenField(x, y);
13418   }
13419
13420   redraw_mask |= REDRAW_FIELD;
13421 }
13422
13423 static boolean canFallDown(struct PlayerInfo *player)
13424 {
13425   int jx = player->jx, jy = player->jy;
13426
13427   return (IN_LEV_FIELD(jx, jy + 1) &&
13428           (IS_FREE(jx, jy + 1) ||
13429            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13430           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13431           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13432 }
13433
13434 static boolean canPassField(int x, int y, int move_dir)
13435 {
13436   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13437   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13438   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13439   int nextx = x + dx;
13440   int nexty = y + dy;
13441   int element = Feld[x][y];
13442
13443   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13444           !CAN_MOVE(element) &&
13445           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13446           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13447           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13448 }
13449
13450 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13451 {
13452   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13453   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13454   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13455   int newx = x + dx;
13456   int newy = y + dy;
13457
13458   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13459           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13460           (IS_DIGGABLE(Feld[newx][newy]) ||
13461            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13462            canPassField(newx, newy, move_dir)));
13463 }
13464
13465 static void CheckGravityMovement(struct PlayerInfo *player)
13466 {
13467 #if USE_PLAYER_GRAVITY
13468   if (player->gravity && !player->programmed_action)
13469 #else
13470   if (game.gravity && !player->programmed_action)
13471 #endif
13472   {
13473     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13474     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13475     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13476     int jx = player->jx, jy = player->jy;
13477     boolean player_is_moving_to_valid_field =
13478       (!player_is_snapping &&
13479        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13480         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13481     boolean player_can_fall_down = canFallDown(player);
13482
13483     if (player_can_fall_down &&
13484         !player_is_moving_to_valid_field)
13485       player->programmed_action = MV_DOWN;
13486   }
13487 }
13488
13489 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13490 {
13491   return CheckGravityMovement(player);
13492
13493 #if USE_PLAYER_GRAVITY
13494   if (player->gravity && !player->programmed_action)
13495 #else
13496   if (game.gravity && !player->programmed_action)
13497 #endif
13498   {
13499     int jx = player->jx, jy = player->jy;
13500     boolean field_under_player_is_free =
13501       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13502     boolean player_is_standing_on_valid_field =
13503       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13504        (IS_WALKABLE(Feld[jx][jy]) &&
13505         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13506
13507     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13508       player->programmed_action = MV_DOWN;
13509   }
13510 }
13511
13512 /*
13513   MovePlayerOneStep()
13514   -----------------------------------------------------------------------------
13515   dx, dy:               direction (non-diagonal) to try to move the player to
13516   real_dx, real_dy:     direction as read from input device (can be diagonal)
13517 */
13518
13519 boolean MovePlayerOneStep(struct PlayerInfo *player,
13520                           int dx, int dy, int real_dx, int real_dy)
13521 {
13522   int jx = player->jx, jy = player->jy;
13523   int new_jx = jx + dx, new_jy = jy + dy;
13524 #if !USE_FIXED_DONT_RUN_INTO
13525   int element;
13526 #endif
13527   int can_move;
13528   boolean player_can_move = !player->cannot_move;
13529
13530   if (!player->active || (!dx && !dy))
13531     return MP_NO_ACTION;
13532
13533   player->MovDir = (dx < 0 ? MV_LEFT :
13534                     dx > 0 ? MV_RIGHT :
13535                     dy < 0 ? MV_UP :
13536                     dy > 0 ? MV_DOWN :  MV_NONE);
13537
13538   if (!IN_LEV_FIELD(new_jx, new_jy))
13539     return MP_NO_ACTION;
13540
13541   if (!player_can_move)
13542   {
13543     if (player->MovPos == 0)
13544     {
13545       player->is_moving = FALSE;
13546       player->is_digging = FALSE;
13547       player->is_collecting = FALSE;
13548       player->is_snapping = FALSE;
13549       player->is_pushing = FALSE;
13550     }
13551   }
13552
13553 #if 1
13554   if (!options.network && game.centered_player_nr == -1 &&
13555       !AllPlayersInSight(player, new_jx, new_jy))
13556     return MP_NO_ACTION;
13557 #else
13558   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13559     return MP_NO_ACTION;
13560 #endif
13561
13562 #if !USE_FIXED_DONT_RUN_INTO
13563   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13564
13565   /* (moved to DigField()) */
13566   if (player_can_move && DONT_RUN_INTO(element))
13567   {
13568     if (element == EL_ACID && dx == 0 && dy == 1)
13569     {
13570       SplashAcid(new_jx, new_jy);
13571       Feld[jx][jy] = EL_PLAYER_1;
13572       InitMovingField(jx, jy, MV_DOWN);
13573       Store[jx][jy] = EL_ACID;
13574       ContinueMoving(jx, jy);
13575       BuryPlayer(player);
13576     }
13577     else
13578       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13579
13580     return MP_MOVING;
13581   }
13582 #endif
13583
13584   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13585   if (can_move != MP_MOVING)
13586     return can_move;
13587
13588   /* check if DigField() has caused relocation of the player */
13589   if (player->jx != jx || player->jy != jy)
13590     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13591
13592   StorePlayer[jx][jy] = 0;
13593   player->last_jx = jx;
13594   player->last_jy = jy;
13595   player->jx = new_jx;
13596   player->jy = new_jy;
13597   StorePlayer[new_jx][new_jy] = player->element_nr;
13598
13599   if (player->move_delay_value_next != -1)
13600   {
13601     player->move_delay_value = player->move_delay_value_next;
13602     player->move_delay_value_next = -1;
13603   }
13604
13605   player->MovPos =
13606     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13607
13608   player->step_counter++;
13609
13610   PlayerVisit[jx][jy] = FrameCounter;
13611
13612 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13613   player->is_moving = TRUE;
13614 #endif
13615
13616 #if 1
13617   /* should better be called in MovePlayer(), but this breaks some tapes */
13618   ScrollPlayer(player, SCROLL_INIT);
13619 #endif
13620
13621   return MP_MOVING;
13622 }
13623
13624 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13625 {
13626   int jx = player->jx, jy = player->jy;
13627   int old_jx = jx, old_jy = jy;
13628   int moved = MP_NO_ACTION;
13629
13630   if (!player->active)
13631     return FALSE;
13632
13633   if (!dx && !dy)
13634   {
13635     if (player->MovPos == 0)
13636     {
13637       player->is_moving = FALSE;
13638       player->is_digging = FALSE;
13639       player->is_collecting = FALSE;
13640       player->is_snapping = FALSE;
13641       player->is_pushing = FALSE;
13642     }
13643
13644     return FALSE;
13645   }
13646
13647   if (player->move_delay > 0)
13648     return FALSE;
13649
13650   player->move_delay = -1;              /* set to "uninitialized" value */
13651
13652   /* store if player is automatically moved to next field */
13653   player->is_auto_moving = (player->programmed_action != MV_NONE);
13654
13655   /* remove the last programmed player action */
13656   player->programmed_action = 0;
13657
13658   if (player->MovPos)
13659   {
13660     /* should only happen if pre-1.2 tape recordings are played */
13661     /* this is only for backward compatibility */
13662
13663     int original_move_delay_value = player->move_delay_value;
13664
13665 #if DEBUG
13666     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13667            tape.counter);
13668 #endif
13669
13670     /* scroll remaining steps with finest movement resolution */
13671     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13672
13673     while (player->MovPos)
13674     {
13675       ScrollPlayer(player, SCROLL_GO_ON);
13676       ScrollScreen(NULL, SCROLL_GO_ON);
13677
13678       AdvanceFrameAndPlayerCounters(player->index_nr);
13679
13680       DrawAllPlayers();
13681       BackToFront();
13682     }
13683
13684     player->move_delay_value = original_move_delay_value;
13685   }
13686
13687   player->is_active = FALSE;
13688
13689   if (player->last_move_dir & MV_HORIZONTAL)
13690   {
13691     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13692       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13693   }
13694   else
13695   {
13696     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13697       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13698   }
13699
13700 #if USE_FIXED_BORDER_RUNNING_GFX
13701   if (!moved && !player->is_active)
13702   {
13703     player->is_moving = FALSE;
13704     player->is_digging = FALSE;
13705     player->is_collecting = FALSE;
13706     player->is_snapping = FALSE;
13707     player->is_pushing = FALSE;
13708   }
13709 #endif
13710
13711   jx = player->jx;
13712   jy = player->jy;
13713
13714 #if 1
13715   if (moved & MP_MOVING && !ScreenMovPos &&
13716       (player->index_nr == game.centered_player_nr ||
13717        game.centered_player_nr == -1))
13718 #else
13719   if (moved & MP_MOVING && !ScreenMovPos &&
13720       (player == local_player || !options.network))
13721 #endif
13722   {
13723     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13724     int offset = game.scroll_delay_value;
13725
13726     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13727     {
13728       /* actual player has left the screen -- scroll in that direction */
13729       if (jx != old_jx)         /* player has moved horizontally */
13730         scroll_x += (jx - old_jx);
13731       else                      /* player has moved vertically */
13732         scroll_y += (jy - old_jy);
13733     }
13734     else
13735     {
13736       if (jx != old_jx)         /* player has moved horizontally */
13737       {
13738         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13739             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13740           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13741
13742         /* don't scroll over playfield boundaries */
13743         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13744           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13745
13746         /* don't scroll more than one field at a time */
13747         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13748
13749         /* don't scroll against the player's moving direction */
13750         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13751             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13752           scroll_x = old_scroll_x;
13753       }
13754       else                      /* player has moved vertically */
13755       {
13756         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13757             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13758           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13759
13760         /* don't scroll over playfield boundaries */
13761         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13762           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13763
13764         /* don't scroll more than one field at a time */
13765         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13766
13767         /* don't scroll against the player's moving direction */
13768         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13769             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13770           scroll_y = old_scroll_y;
13771       }
13772     }
13773
13774     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13775     {
13776 #if 1
13777       if (!options.network && game.centered_player_nr == -1 &&
13778           !AllPlayersInVisibleScreen())
13779       {
13780         scroll_x = old_scroll_x;
13781         scroll_y = old_scroll_y;
13782       }
13783       else
13784 #else
13785       if (!options.network && !AllPlayersInVisibleScreen())
13786       {
13787         scroll_x = old_scroll_x;
13788         scroll_y = old_scroll_y;
13789       }
13790       else
13791 #endif
13792       {
13793         ScrollScreen(player, SCROLL_INIT);
13794         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13795       }
13796     }
13797   }
13798
13799   player->StepFrame = 0;
13800
13801   if (moved & MP_MOVING)
13802   {
13803     if (old_jx != jx && old_jy == jy)
13804       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13805     else if (old_jx == jx && old_jy != jy)
13806       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13807
13808     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13809
13810     player->last_move_dir = player->MovDir;
13811     player->is_moving = TRUE;
13812     player->is_snapping = FALSE;
13813     player->is_switching = FALSE;
13814     player->is_dropping = FALSE;
13815     player->is_dropping_pressed = FALSE;
13816     player->drop_pressed_delay = 0;
13817
13818 #if 0
13819     /* should better be called here than above, but this breaks some tapes */
13820     ScrollPlayer(player, SCROLL_INIT);
13821 #endif
13822   }
13823   else
13824   {
13825     CheckGravityMovementWhenNotMoving(player);
13826
13827     player->is_moving = FALSE;
13828
13829     /* at this point, the player is allowed to move, but cannot move right now
13830        (e.g. because of something blocking the way) -- ensure that the player
13831        is also allowed to move in the next frame (in old versions before 3.1.1,
13832        the player was forced to wait again for eight frames before next try) */
13833
13834     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13835       player->move_delay = 0;   /* allow direct movement in the next frame */
13836   }
13837
13838   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13839     player->move_delay = player->move_delay_value;
13840
13841   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13842   {
13843     TestIfPlayerTouchesBadThing(jx, jy);
13844     TestIfPlayerTouchesCustomElement(jx, jy);
13845   }
13846
13847   if (!player->active)
13848     RemovePlayer(player);
13849
13850   return moved;
13851 }
13852
13853 void ScrollPlayer(struct PlayerInfo *player, int mode)
13854 {
13855   int jx = player->jx, jy = player->jy;
13856   int last_jx = player->last_jx, last_jy = player->last_jy;
13857   int move_stepsize = TILEX / player->move_delay_value;
13858
13859 #if USE_NEW_PLAYER_SPEED
13860   if (!player->active)
13861     return;
13862
13863   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13864     return;
13865 #else
13866   if (!player->active || player->MovPos == 0)
13867     return;
13868 #endif
13869
13870   if (mode == SCROLL_INIT)
13871   {
13872     player->actual_frame_counter = FrameCounter;
13873     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13874
13875     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13876         Feld[last_jx][last_jy] == EL_EMPTY)
13877     {
13878       int last_field_block_delay = 0;   /* start with no blocking at all */
13879       int block_delay_adjustment = player->block_delay_adjustment;
13880
13881       /* if player blocks last field, add delay for exactly one move */
13882       if (player->block_last_field)
13883       {
13884         last_field_block_delay += player->move_delay_value;
13885
13886         /* when blocking enabled, prevent moving up despite gravity */
13887 #if USE_PLAYER_GRAVITY
13888         if (player->gravity && player->MovDir == MV_UP)
13889           block_delay_adjustment = -1;
13890 #else
13891         if (game.gravity && player->MovDir == MV_UP)
13892           block_delay_adjustment = -1;
13893 #endif
13894       }
13895
13896       /* add block delay adjustment (also possible when not blocking) */
13897       last_field_block_delay += block_delay_adjustment;
13898
13899       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13900       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13901     }
13902
13903 #if USE_NEW_PLAYER_SPEED
13904     if (player->MovPos != 0)    /* player has not yet reached destination */
13905       return;
13906 #else
13907     return;
13908 #endif
13909   }
13910   else if (!FrameReached(&player->actual_frame_counter, 1))
13911     return;
13912
13913 #if USE_NEW_PLAYER_SPEED
13914   if (player->MovPos != 0)
13915   {
13916     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13917     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13918
13919     /* before DrawPlayer() to draw correct player graphic for this case */
13920     if (player->MovPos == 0)
13921       CheckGravityMovement(player);
13922   }
13923 #else
13924   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13925   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13926
13927   /* before DrawPlayer() to draw correct player graphic for this case */
13928   if (player->MovPos == 0)
13929     CheckGravityMovement(player);
13930 #endif
13931
13932   if (player->MovPos == 0)      /* player reached destination field */
13933   {
13934     if (player->move_delay_reset_counter > 0)
13935     {
13936       player->move_delay_reset_counter--;
13937
13938       if (player->move_delay_reset_counter == 0)
13939       {
13940         /* continue with normal speed after quickly moving through gate */
13941         HALVE_PLAYER_SPEED(player);
13942
13943         /* be able to make the next move without delay */
13944         player->move_delay = 0;
13945       }
13946     }
13947
13948     player->last_jx = jx;
13949     player->last_jy = jy;
13950
13951     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13952         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13953 #if 1
13954         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13955 #endif
13956         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13957         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13958 #if 1
13959         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13960 #endif
13961         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13962         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13963     {
13964       DrawPlayer(player);       /* needed here only to cleanup last field */
13965       RemovePlayer(player);
13966
13967       if (local_player->friends_still_needed == 0 ||
13968           IS_SP_ELEMENT(Feld[jx][jy]))
13969         PlayerWins(player);
13970     }
13971
13972     /* this breaks one level: "machine", level 000 */
13973     {
13974       int move_direction = player->MovDir;
13975       int enter_side = MV_DIR_OPPOSITE(move_direction);
13976       int leave_side = move_direction;
13977       int old_jx = last_jx;
13978       int old_jy = last_jy;
13979       int old_element = Feld[old_jx][old_jy];
13980       int new_element = Feld[jx][jy];
13981
13982       if (IS_CUSTOM_ELEMENT(old_element))
13983         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13984                                    CE_LEFT_BY_PLAYER,
13985                                    player->index_bit, leave_side);
13986
13987       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13988                                           CE_PLAYER_LEAVES_X,
13989                                           player->index_bit, leave_side);
13990
13991       if (IS_CUSTOM_ELEMENT(new_element))
13992         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13993                                    player->index_bit, enter_side);
13994
13995       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13996                                           CE_PLAYER_ENTERS_X,
13997                                           player->index_bit, enter_side);
13998
13999 #if USE_FIX_CE_ACTION_WITH_PLAYER
14000       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14001                                         CE_MOVE_OF_X, move_direction);
14002 #else
14003       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14004                                         CE_MOVE_OF_X, move_direction);
14005 #endif
14006     }
14007
14008     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14009     {
14010       TestIfPlayerTouchesBadThing(jx, jy);
14011       TestIfPlayerTouchesCustomElement(jx, jy);
14012
14013       /* needed because pushed element has not yet reached its destination,
14014          so it would trigger a change event at its previous field location */
14015       if (!player->is_pushing)
14016         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14017
14018       if (!player->active)
14019         RemovePlayer(player);
14020     }
14021
14022     if (!local_player->LevelSolved && level.use_step_counter)
14023     {
14024       int i;
14025
14026       TimePlayed++;
14027
14028       if (TimeLeft > 0)
14029       {
14030         TimeLeft--;
14031
14032         if (TimeLeft <= 10 && setup.time_limit)
14033           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14034
14035 #if 1
14036         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14037
14038         DisplayGameControlValues();
14039 #else
14040         DrawGameValue_Time(TimeLeft);
14041 #endif
14042
14043         if (!TimeLeft && setup.time_limit)
14044           for (i = 0; i < MAX_PLAYERS; i++)
14045             KillPlayer(&stored_player[i]);
14046       }
14047 #if 1
14048       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14049       {
14050         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14051
14052         DisplayGameControlValues();
14053       }
14054 #else
14055       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14056         DrawGameValue_Time(TimePlayed);
14057 #endif
14058     }
14059
14060     if (tape.single_step && tape.recording && !tape.pausing &&
14061         !player->programmed_action)
14062       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14063   }
14064 }
14065
14066 void ScrollScreen(struct PlayerInfo *player, int mode)
14067 {
14068   static unsigned long screen_frame_counter = 0;
14069
14070   if (mode == SCROLL_INIT)
14071   {
14072     /* set scrolling step size according to actual player's moving speed */
14073     ScrollStepSize = TILEX / player->move_delay_value;
14074
14075     screen_frame_counter = FrameCounter;
14076     ScreenMovDir = player->MovDir;
14077     ScreenMovPos = player->MovPos;
14078     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14079     return;
14080   }
14081   else if (!FrameReached(&screen_frame_counter, 1))
14082     return;
14083
14084   if (ScreenMovPos)
14085   {
14086     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14087     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14088     redraw_mask |= REDRAW_FIELD;
14089   }
14090   else
14091     ScreenMovDir = MV_NONE;
14092 }
14093
14094 void TestIfPlayerTouchesCustomElement(int x, int y)
14095 {
14096   static int xy[4][2] =
14097   {
14098     { 0, -1 },
14099     { -1, 0 },
14100     { +1, 0 },
14101     { 0, +1 }
14102   };
14103   static int trigger_sides[4][2] =
14104   {
14105     /* center side       border side */
14106     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14107     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14108     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14109     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14110   };
14111   static int touch_dir[4] =
14112   {
14113     MV_LEFT | MV_RIGHT,
14114     MV_UP   | MV_DOWN,
14115     MV_UP   | MV_DOWN,
14116     MV_LEFT | MV_RIGHT
14117   };
14118   int center_element = Feld[x][y];      /* should always be non-moving! */
14119   int i;
14120
14121   for (i = 0; i < NUM_DIRECTIONS; i++)
14122   {
14123     int xx = x + xy[i][0];
14124     int yy = y + xy[i][1];
14125     int center_side = trigger_sides[i][0];
14126     int border_side = trigger_sides[i][1];
14127     int border_element;
14128
14129     if (!IN_LEV_FIELD(xx, yy))
14130       continue;
14131
14132     if (IS_PLAYER(x, y))                /* player found at center element */
14133     {
14134       struct PlayerInfo *player = PLAYERINFO(x, y);
14135
14136       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14137         border_element = Feld[xx][yy];          /* may be moving! */
14138       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14139         border_element = Feld[xx][yy];
14140       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14141         border_element = MovingOrBlocked2Element(xx, yy);
14142       else
14143         continue;               /* center and border element do not touch */
14144
14145       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14146                                  player->index_bit, border_side);
14147       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14148                                           CE_PLAYER_TOUCHES_X,
14149                                           player->index_bit, border_side);
14150
14151 #if USE_FIX_CE_ACTION_WITH_PLAYER
14152       {
14153         /* use player element that is initially defined in the level playfield,
14154            not the player element that corresponds to the runtime player number
14155            (example: a level that contains EL_PLAYER_3 as the only player would
14156            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14157         int player_element = PLAYERINFO(x, y)->initial_element;
14158
14159         CheckElementChangeBySide(xx, yy, border_element, player_element,
14160                                  CE_TOUCHING_X, border_side);
14161       }
14162 #endif
14163     }
14164     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14165     {
14166       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14167
14168       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14169       {
14170         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14171           continue;             /* center and border element do not touch */
14172       }
14173
14174       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14175                                  player->index_bit, center_side);
14176       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14177                                           CE_PLAYER_TOUCHES_X,
14178                                           player->index_bit, center_side);
14179
14180 #if USE_FIX_CE_ACTION_WITH_PLAYER
14181       {
14182         /* use player element that is initially defined in the level playfield,
14183            not the player element that corresponds to the runtime player number
14184            (example: a level that contains EL_PLAYER_3 as the only player would
14185            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14186         int player_element = PLAYERINFO(xx, yy)->initial_element;
14187
14188         CheckElementChangeBySide(x, y, center_element, player_element,
14189                                  CE_TOUCHING_X, center_side);
14190       }
14191 #endif
14192
14193       break;
14194     }
14195   }
14196 }
14197
14198 #if USE_ELEMENT_TOUCHING_BUGFIX
14199
14200 void TestIfElementTouchesCustomElement(int x, int y)
14201 {
14202   static int xy[4][2] =
14203   {
14204     { 0, -1 },
14205     { -1, 0 },
14206     { +1, 0 },
14207     { 0, +1 }
14208   };
14209   static int trigger_sides[4][2] =
14210   {
14211     /* center side      border side */
14212     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14213     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14214     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14215     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14216   };
14217   static int touch_dir[4] =
14218   {
14219     MV_LEFT | MV_RIGHT,
14220     MV_UP   | MV_DOWN,
14221     MV_UP   | MV_DOWN,
14222     MV_LEFT | MV_RIGHT
14223   };
14224   boolean change_center_element = FALSE;
14225   int center_element = Feld[x][y];      /* should always be non-moving! */
14226   int border_element_old[NUM_DIRECTIONS];
14227   int i;
14228
14229   for (i = 0; i < NUM_DIRECTIONS; i++)
14230   {
14231     int xx = x + xy[i][0];
14232     int yy = y + xy[i][1];
14233     int border_element;
14234
14235     border_element_old[i] = -1;
14236
14237     if (!IN_LEV_FIELD(xx, yy))
14238       continue;
14239
14240     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14241       border_element = Feld[xx][yy];    /* may be moving! */
14242     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14243       border_element = Feld[xx][yy];
14244     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14245       border_element = MovingOrBlocked2Element(xx, yy);
14246     else
14247       continue;                 /* center and border element do not touch */
14248
14249     border_element_old[i] = border_element;
14250   }
14251
14252   for (i = 0; i < NUM_DIRECTIONS; i++)
14253   {
14254     int xx = x + xy[i][0];
14255     int yy = y + xy[i][1];
14256     int center_side = trigger_sides[i][0];
14257     int border_element = border_element_old[i];
14258
14259     if (border_element == -1)
14260       continue;
14261
14262     /* check for change of border element */
14263     CheckElementChangeBySide(xx, yy, border_element, center_element,
14264                              CE_TOUCHING_X, center_side);
14265
14266     /* (center element cannot be player, so we dont have to check this here) */
14267   }
14268
14269   for (i = 0; i < NUM_DIRECTIONS; i++)
14270   {
14271     int xx = x + xy[i][0];
14272     int yy = y + xy[i][1];
14273     int border_side = trigger_sides[i][1];
14274     int border_element = border_element_old[i];
14275
14276     if (border_element == -1)
14277       continue;
14278
14279     /* check for change of center element (but change it only once) */
14280     if (!change_center_element)
14281       change_center_element =
14282         CheckElementChangeBySide(x, y, center_element, border_element,
14283                                  CE_TOUCHING_X, border_side);
14284
14285 #if USE_FIX_CE_ACTION_WITH_PLAYER
14286     if (IS_PLAYER(xx, yy))
14287     {
14288       /* use player element that is initially defined in the level playfield,
14289          not the player element that corresponds to the runtime player number
14290          (example: a level that contains EL_PLAYER_3 as the only player would
14291          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14292       int player_element = PLAYERINFO(xx, yy)->initial_element;
14293
14294       CheckElementChangeBySide(x, y, center_element, player_element,
14295                                CE_TOUCHING_X, border_side);
14296     }
14297 #endif
14298   }
14299 }
14300
14301 #else
14302
14303 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14304 {
14305   static int xy[4][2] =
14306   {
14307     { 0, -1 },
14308     { -1, 0 },
14309     { +1, 0 },
14310     { 0, +1 }
14311   };
14312   static int trigger_sides[4][2] =
14313   {
14314     /* center side      border side */
14315     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14316     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14317     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14318     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14319   };
14320   static int touch_dir[4] =
14321   {
14322     MV_LEFT | MV_RIGHT,
14323     MV_UP   | MV_DOWN,
14324     MV_UP   | MV_DOWN,
14325     MV_LEFT | MV_RIGHT
14326   };
14327   boolean change_center_element = FALSE;
14328   int center_element = Feld[x][y];      /* should always be non-moving! */
14329   int i;
14330
14331   for (i = 0; i < NUM_DIRECTIONS; i++)
14332   {
14333     int xx = x + xy[i][0];
14334     int yy = y + xy[i][1];
14335     int center_side = trigger_sides[i][0];
14336     int border_side = trigger_sides[i][1];
14337     int border_element;
14338
14339     if (!IN_LEV_FIELD(xx, yy))
14340       continue;
14341
14342     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14343       border_element = Feld[xx][yy];    /* may be moving! */
14344     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14345       border_element = Feld[xx][yy];
14346     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14347       border_element = MovingOrBlocked2Element(xx, yy);
14348     else
14349       continue;                 /* center and border element do not touch */
14350
14351     /* check for change of center element (but change it only once) */
14352     if (!change_center_element)
14353       change_center_element =
14354         CheckElementChangeBySide(x, y, center_element, border_element,
14355                                  CE_TOUCHING_X, border_side);
14356
14357     /* check for change of border element */
14358     CheckElementChangeBySide(xx, yy, border_element, center_element,
14359                              CE_TOUCHING_X, center_side);
14360   }
14361 }
14362
14363 #endif
14364
14365 void TestIfElementHitsCustomElement(int x, int y, int direction)
14366 {
14367   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14368   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14369   int hitx = x + dx, hity = y + dy;
14370   int hitting_element = Feld[x][y];
14371   int touched_element;
14372
14373   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14374     return;
14375
14376   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14377                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14378
14379   if (IN_LEV_FIELD(hitx, hity))
14380   {
14381     int opposite_direction = MV_DIR_OPPOSITE(direction);
14382     int hitting_side = direction;
14383     int touched_side = opposite_direction;
14384     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14385                           MovDir[hitx][hity] != direction ||
14386                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14387
14388     object_hit = TRUE;
14389
14390     if (object_hit)
14391     {
14392       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14393                                CE_HITTING_X, touched_side);
14394
14395       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14396                                CE_HIT_BY_X, hitting_side);
14397
14398       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14399                                CE_HIT_BY_SOMETHING, opposite_direction);
14400
14401 #if USE_FIX_CE_ACTION_WITH_PLAYER
14402       if (IS_PLAYER(hitx, hity))
14403       {
14404         /* use player element that is initially defined in the level playfield,
14405            not the player element that corresponds to the runtime player number
14406            (example: a level that contains EL_PLAYER_3 as the only player would
14407            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14408         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14409
14410         CheckElementChangeBySide(x, y, hitting_element, player_element,
14411                                  CE_HITTING_X, touched_side);
14412       }
14413 #endif
14414     }
14415   }
14416
14417   /* "hitting something" is also true when hitting the playfield border */
14418   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14419                            CE_HITTING_SOMETHING, direction);
14420 }
14421
14422 #if 0
14423 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14424 {
14425   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14426   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14427   int hitx = x + dx, hity = y + dy;
14428   int hitting_element = Feld[x][y];
14429   int touched_element;
14430 #if 0
14431   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14432                         !IS_FREE(hitx, hity) &&
14433                         (!IS_MOVING(hitx, hity) ||
14434                          MovDir[hitx][hity] != direction ||
14435                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14436 #endif
14437
14438   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14439     return;
14440
14441 #if 0
14442   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14443     return;
14444 #endif
14445
14446   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14447                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14448
14449   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14450                            EP_CAN_SMASH_EVERYTHING, direction);
14451
14452   if (IN_LEV_FIELD(hitx, hity))
14453   {
14454     int opposite_direction = MV_DIR_OPPOSITE(direction);
14455     int hitting_side = direction;
14456     int touched_side = opposite_direction;
14457 #if 0
14458     int touched_element = MovingOrBlocked2Element(hitx, hity);
14459 #endif
14460 #if 1
14461     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14462                           MovDir[hitx][hity] != direction ||
14463                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14464
14465     object_hit = TRUE;
14466 #endif
14467
14468     if (object_hit)
14469     {
14470       int i;
14471
14472       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14473                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14474
14475       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14476                                CE_OTHER_IS_SMASHING, touched_side);
14477
14478       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14479                                CE_OTHER_GETS_SMASHED, hitting_side);
14480     }
14481   }
14482 }
14483 #endif
14484
14485 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14486 {
14487   int i, kill_x = -1, kill_y = -1;
14488
14489   int bad_element = -1;
14490   static int test_xy[4][2] =
14491   {
14492     { 0, -1 },
14493     { -1, 0 },
14494     { +1, 0 },
14495     { 0, +1 }
14496   };
14497   static int test_dir[4] =
14498   {
14499     MV_UP,
14500     MV_LEFT,
14501     MV_RIGHT,
14502     MV_DOWN
14503   };
14504
14505   for (i = 0; i < NUM_DIRECTIONS; i++)
14506   {
14507     int test_x, test_y, test_move_dir, test_element;
14508
14509     test_x = good_x + test_xy[i][0];
14510     test_y = good_y + test_xy[i][1];
14511
14512     if (!IN_LEV_FIELD(test_x, test_y))
14513       continue;
14514
14515     test_move_dir =
14516       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14517
14518     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14519
14520     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14521        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14522     */
14523     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14524         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14525     {
14526       kill_x = test_x;
14527       kill_y = test_y;
14528       bad_element = test_element;
14529
14530       break;
14531     }
14532   }
14533
14534   if (kill_x != -1 || kill_y != -1)
14535   {
14536     if (IS_PLAYER(good_x, good_y))
14537     {
14538       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14539
14540       if (player->shield_deadly_time_left > 0 &&
14541           !IS_INDESTRUCTIBLE(bad_element))
14542         Bang(kill_x, kill_y);
14543       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14544         KillPlayer(player);
14545     }
14546     else
14547       Bang(good_x, good_y);
14548   }
14549 }
14550
14551 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14552 {
14553   int i, kill_x = -1, kill_y = -1;
14554   int bad_element = Feld[bad_x][bad_y];
14555   static int test_xy[4][2] =
14556   {
14557     { 0, -1 },
14558     { -1, 0 },
14559     { +1, 0 },
14560     { 0, +1 }
14561   };
14562   static int touch_dir[4] =
14563   {
14564     MV_LEFT | MV_RIGHT,
14565     MV_UP   | MV_DOWN,
14566     MV_UP   | MV_DOWN,
14567     MV_LEFT | MV_RIGHT
14568   };
14569   static int test_dir[4] =
14570   {
14571     MV_UP,
14572     MV_LEFT,
14573     MV_RIGHT,
14574     MV_DOWN
14575   };
14576
14577   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14578     return;
14579
14580   for (i = 0; i < NUM_DIRECTIONS; i++)
14581   {
14582     int test_x, test_y, test_move_dir, test_element;
14583
14584     test_x = bad_x + test_xy[i][0];
14585     test_y = bad_y + test_xy[i][1];
14586
14587     if (!IN_LEV_FIELD(test_x, test_y))
14588       continue;
14589
14590     test_move_dir =
14591       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14592
14593     test_element = Feld[test_x][test_y];
14594
14595     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14596        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14597     */
14598     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14599         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14600     {
14601       /* good thing is player or penguin that does not move away */
14602       if (IS_PLAYER(test_x, test_y))
14603       {
14604         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14605
14606         if (bad_element == EL_ROBOT && player->is_moving)
14607           continue;     /* robot does not kill player if he is moving */
14608
14609         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14610         {
14611           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14612             continue;           /* center and border element do not touch */
14613         }
14614
14615         kill_x = test_x;
14616         kill_y = test_y;
14617
14618         break;
14619       }
14620       else if (test_element == EL_PENGUIN)
14621       {
14622         kill_x = test_x;
14623         kill_y = test_y;
14624
14625         break;
14626       }
14627     }
14628   }
14629
14630   if (kill_x != -1 || kill_y != -1)
14631   {
14632     if (IS_PLAYER(kill_x, kill_y))
14633     {
14634       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14635
14636       if (player->shield_deadly_time_left > 0 &&
14637           !IS_INDESTRUCTIBLE(bad_element))
14638         Bang(bad_x, bad_y);
14639       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14640         KillPlayer(player);
14641     }
14642     else
14643       Bang(kill_x, kill_y);
14644   }
14645 }
14646
14647 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14648 {
14649   int bad_element = Feld[bad_x][bad_y];
14650   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14651   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14652   int test_x = bad_x + dx, test_y = bad_y + dy;
14653   int test_move_dir, test_element;
14654   int kill_x = -1, kill_y = -1;
14655
14656   if (!IN_LEV_FIELD(test_x, test_y))
14657     return;
14658
14659   test_move_dir =
14660     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14661
14662   test_element = Feld[test_x][test_y];
14663
14664   if (test_move_dir != bad_move_dir)
14665   {
14666     /* good thing can be player or penguin that does not move away */
14667     if (IS_PLAYER(test_x, test_y))
14668     {
14669       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14670
14671       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14672          player as being hit when he is moving towards the bad thing, because
14673          the "get hit by" condition would be lost after the player stops) */
14674       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14675         return;         /* player moves away from bad thing */
14676
14677       kill_x = test_x;
14678       kill_y = test_y;
14679     }
14680     else if (test_element == EL_PENGUIN)
14681     {
14682       kill_x = test_x;
14683       kill_y = test_y;
14684     }
14685   }
14686
14687   if (kill_x != -1 || kill_y != -1)
14688   {
14689     if (IS_PLAYER(kill_x, kill_y))
14690     {
14691       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14692
14693       if (player->shield_deadly_time_left > 0 &&
14694           !IS_INDESTRUCTIBLE(bad_element))
14695         Bang(bad_x, bad_y);
14696       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14697         KillPlayer(player);
14698     }
14699     else
14700       Bang(kill_x, kill_y);
14701   }
14702 }
14703
14704 void TestIfPlayerTouchesBadThing(int x, int y)
14705 {
14706   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14707 }
14708
14709 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14710 {
14711   TestIfGoodThingHitsBadThing(x, y, move_dir);
14712 }
14713
14714 void TestIfBadThingTouchesPlayer(int x, int y)
14715 {
14716   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14717 }
14718
14719 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14720 {
14721   TestIfBadThingHitsGoodThing(x, y, move_dir);
14722 }
14723
14724 void TestIfFriendTouchesBadThing(int x, int y)
14725 {
14726   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14727 }
14728
14729 void TestIfBadThingTouchesFriend(int x, int y)
14730 {
14731   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14732 }
14733
14734 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14735 {
14736   int i, kill_x = bad_x, kill_y = bad_y;
14737   static int xy[4][2] =
14738   {
14739     { 0, -1 },
14740     { -1, 0 },
14741     { +1, 0 },
14742     { 0, +1 }
14743   };
14744
14745   for (i = 0; i < NUM_DIRECTIONS; i++)
14746   {
14747     int x, y, element;
14748
14749     x = bad_x + xy[i][0];
14750     y = bad_y + xy[i][1];
14751     if (!IN_LEV_FIELD(x, y))
14752       continue;
14753
14754     element = Feld[x][y];
14755     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14756         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14757     {
14758       kill_x = x;
14759       kill_y = y;
14760       break;
14761     }
14762   }
14763
14764   if (kill_x != bad_x || kill_y != bad_y)
14765     Bang(bad_x, bad_y);
14766 }
14767
14768 void KillPlayer(struct PlayerInfo *player)
14769 {
14770   int jx = player->jx, jy = player->jy;
14771
14772   if (!player->active)
14773     return;
14774
14775 #if 0
14776   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14777          player->killed, player->active, player->reanimated);
14778 #endif
14779
14780   /* the following code was introduced to prevent an infinite loop when calling
14781      -> Bang()
14782      -> CheckTriggeredElementChangeExt()
14783      -> ExecuteCustomElementAction()
14784      -> KillPlayer()
14785      -> (infinitely repeating the above sequence of function calls)
14786      which occurs when killing the player while having a CE with the setting
14787      "kill player X when explosion of <player X>"; the solution using a new
14788      field "player->killed" was chosen for backwards compatibility, although
14789      clever use of the fields "player->active" etc. would probably also work */
14790 #if 1
14791   if (player->killed)
14792     return;
14793 #endif
14794
14795   player->killed = TRUE;
14796
14797   /* remove accessible field at the player's position */
14798   Feld[jx][jy] = EL_EMPTY;
14799
14800   /* deactivate shield (else Bang()/Explode() would not work right) */
14801   player->shield_normal_time_left = 0;
14802   player->shield_deadly_time_left = 0;
14803
14804 #if 0
14805   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14806          player->killed, player->active, player->reanimated);
14807 #endif
14808
14809   Bang(jx, jy);
14810
14811 #if 0
14812   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14813          player->killed, player->active, player->reanimated);
14814 #endif
14815
14816 #if USE_PLAYER_REANIMATION
14817 #if 1
14818   if (player->reanimated)       /* killed player may have been reanimated */
14819     player->killed = player->reanimated = FALSE;
14820   else
14821     BuryPlayer(player);
14822 #else
14823   if (player->killed)           /* player may have been reanimated */
14824     BuryPlayer(player);
14825 #endif
14826 #else
14827   BuryPlayer(player);
14828 #endif
14829 }
14830
14831 static void KillPlayerUnlessEnemyProtected(int x, int y)
14832 {
14833   if (!PLAYER_ENEMY_PROTECTED(x, y))
14834     KillPlayer(PLAYERINFO(x, y));
14835 }
14836
14837 static void KillPlayerUnlessExplosionProtected(int x, int y)
14838 {
14839   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14840     KillPlayer(PLAYERINFO(x, y));
14841 }
14842
14843 void BuryPlayer(struct PlayerInfo *player)
14844 {
14845   int jx = player->jx, jy = player->jy;
14846
14847   if (!player->active)
14848     return;
14849
14850   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14851   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14852
14853   player->GameOver = TRUE;
14854   RemovePlayer(player);
14855 }
14856
14857 void RemovePlayer(struct PlayerInfo *player)
14858 {
14859   int jx = player->jx, jy = player->jy;
14860   int i, found = FALSE;
14861
14862   player->present = FALSE;
14863   player->active = FALSE;
14864
14865   if (!ExplodeField[jx][jy])
14866     StorePlayer[jx][jy] = 0;
14867
14868   if (player->is_moving)
14869     TEST_DrawLevelField(player->last_jx, player->last_jy);
14870
14871   for (i = 0; i < MAX_PLAYERS; i++)
14872     if (stored_player[i].active)
14873       found = TRUE;
14874
14875   if (!found)
14876     AllPlayersGone = TRUE;
14877
14878   ExitX = ZX = jx;
14879   ExitY = ZY = jy;
14880 }
14881
14882 #if USE_NEW_SNAP_DELAY
14883 static void setFieldForSnapping(int x, int y, int element, int direction)
14884 {
14885   struct ElementInfo *ei = &element_info[element];
14886   int direction_bit = MV_DIR_TO_BIT(direction);
14887   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14888   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14889                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14890
14891   Feld[x][y] = EL_ELEMENT_SNAPPING;
14892   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14893
14894   ResetGfxAnimation(x, y);
14895
14896   GfxElement[x][y] = element;
14897   GfxAction[x][y] = action;
14898   GfxDir[x][y] = direction;
14899   GfxFrame[x][y] = -1;
14900 }
14901 #endif
14902
14903 /*
14904   =============================================================================
14905   checkDiagonalPushing()
14906   -----------------------------------------------------------------------------
14907   check if diagonal input device direction results in pushing of object
14908   (by checking if the alternative direction is walkable, diggable, ...)
14909   =============================================================================
14910 */
14911
14912 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14913                                     int x, int y, int real_dx, int real_dy)
14914 {
14915   int jx, jy, dx, dy, xx, yy;
14916
14917   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14918     return TRUE;
14919
14920   /* diagonal direction: check alternative direction */
14921   jx = player->jx;
14922   jy = player->jy;
14923   dx = x - jx;
14924   dy = y - jy;
14925   xx = jx + (dx == 0 ? real_dx : 0);
14926   yy = jy + (dy == 0 ? real_dy : 0);
14927
14928   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14929 }
14930
14931 /*
14932   =============================================================================
14933   DigField()
14934   -----------------------------------------------------------------------------
14935   x, y:                 field next to player (non-diagonal) to try to dig to
14936   real_dx, real_dy:     direction as read from input device (can be diagonal)
14937   =============================================================================
14938 */
14939
14940 static int DigField(struct PlayerInfo *player,
14941                     int oldx, int oldy, int x, int y,
14942                     int real_dx, int real_dy, int mode)
14943 {
14944   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14945   boolean player_was_pushing = player->is_pushing;
14946   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14947   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14948   int jx = oldx, jy = oldy;
14949   int dx = x - jx, dy = y - jy;
14950   int nextx = x + dx, nexty = y + dy;
14951   int move_direction = (dx == -1 ? MV_LEFT  :
14952                         dx == +1 ? MV_RIGHT :
14953                         dy == -1 ? MV_UP    :
14954                         dy == +1 ? MV_DOWN  : MV_NONE);
14955   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14956   int dig_side = MV_DIR_OPPOSITE(move_direction);
14957   int old_element = Feld[jx][jy];
14958 #if USE_FIXED_DONT_RUN_INTO
14959   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14960 #else
14961   int element;
14962 #endif
14963   int collect_count;
14964
14965   if (is_player)                /* function can also be called by EL_PENGUIN */
14966   {
14967     if (player->MovPos == 0)
14968     {
14969       player->is_digging = FALSE;
14970       player->is_collecting = FALSE;
14971     }
14972
14973     if (player->MovPos == 0)    /* last pushing move finished */
14974       player->is_pushing = FALSE;
14975
14976     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14977     {
14978       player->is_switching = FALSE;
14979       player->push_delay = -1;
14980
14981       return MP_NO_ACTION;
14982     }
14983   }
14984
14985 #if !USE_FIXED_DONT_RUN_INTO
14986   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14987     return MP_NO_ACTION;
14988 #endif
14989
14990   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14991     old_element = Back[jx][jy];
14992
14993   /* in case of element dropped at player position, check background */
14994   else if (Back[jx][jy] != EL_EMPTY &&
14995            game.engine_version >= VERSION_IDENT(2,2,0,0))
14996     old_element = Back[jx][jy];
14997
14998   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14999     return MP_NO_ACTION;        /* field has no opening in this direction */
15000
15001   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15002     return MP_NO_ACTION;        /* field has no opening in this direction */
15003
15004 #if USE_FIXED_DONT_RUN_INTO
15005   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15006   {
15007     SplashAcid(x, y);
15008
15009     Feld[jx][jy] = player->artwork_element;
15010     InitMovingField(jx, jy, MV_DOWN);
15011     Store[jx][jy] = EL_ACID;
15012     ContinueMoving(jx, jy);
15013     BuryPlayer(player);
15014
15015     return MP_DONT_RUN_INTO;
15016   }
15017 #endif
15018
15019 #if USE_FIXED_DONT_RUN_INTO
15020   if (player_can_move && DONT_RUN_INTO(element))
15021   {
15022     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15023
15024     return MP_DONT_RUN_INTO;
15025   }
15026 #endif
15027
15028 #if USE_FIXED_DONT_RUN_INTO
15029   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15030     return MP_NO_ACTION;
15031 #endif
15032
15033 #if !USE_FIXED_DONT_RUN_INTO
15034   element = Feld[x][y];
15035 #endif
15036
15037   collect_count = element_info[element].collect_count_initial;
15038
15039   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15040     return MP_NO_ACTION;
15041
15042   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15043     player_can_move = player_can_move_or_snap;
15044
15045   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15046       game.engine_version >= VERSION_IDENT(2,2,0,0))
15047   {
15048     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15049                                player->index_bit, dig_side);
15050     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15051                                         player->index_bit, dig_side);
15052
15053     if (element == EL_DC_LANDMINE)
15054       Bang(x, y);
15055
15056     if (Feld[x][y] != element)          /* field changed by snapping */
15057       return MP_ACTION;
15058
15059     return MP_NO_ACTION;
15060   }
15061
15062 #if USE_PLAYER_GRAVITY
15063   if (player->gravity && is_player && !player->is_auto_moving &&
15064       canFallDown(player) && move_direction != MV_DOWN &&
15065       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15066     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15067 #else
15068   if (game.gravity && is_player && !player->is_auto_moving &&
15069       canFallDown(player) && move_direction != MV_DOWN &&
15070       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15071     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15072 #endif
15073
15074   if (player_can_move &&
15075       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15076   {
15077     int sound_element = SND_ELEMENT(element);
15078     int sound_action = ACTION_WALKING;
15079
15080     if (IS_RND_GATE(element))
15081     {
15082       if (!player->key[RND_GATE_NR(element)])
15083         return MP_NO_ACTION;
15084     }
15085     else if (IS_RND_GATE_GRAY(element))
15086     {
15087       if (!player->key[RND_GATE_GRAY_NR(element)])
15088         return MP_NO_ACTION;
15089     }
15090     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15091     {
15092       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15093         return MP_NO_ACTION;
15094     }
15095     else if (element == EL_EXIT_OPEN ||
15096              element == EL_EM_EXIT_OPEN ||
15097 #if 1
15098              element == EL_EM_EXIT_OPENING ||
15099 #endif
15100              element == EL_STEEL_EXIT_OPEN ||
15101              element == EL_EM_STEEL_EXIT_OPEN ||
15102 #if 1
15103              element == EL_EM_STEEL_EXIT_OPENING ||
15104 #endif
15105              element == EL_SP_EXIT_OPEN ||
15106              element == EL_SP_EXIT_OPENING)
15107     {
15108       sound_action = ACTION_PASSING;    /* player is passing exit */
15109     }
15110     else if (element == EL_EMPTY)
15111     {
15112       sound_action = ACTION_MOVING;             /* nothing to walk on */
15113     }
15114
15115     /* play sound from background or player, whatever is available */
15116     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15117       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15118     else
15119       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15120   }
15121   else if (player_can_move &&
15122            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15123   {
15124     if (!ACCESS_FROM(element, opposite_direction))
15125       return MP_NO_ACTION;      /* field not accessible from this direction */
15126
15127     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15128       return MP_NO_ACTION;
15129
15130     if (IS_EM_GATE(element))
15131     {
15132       if (!player->key[EM_GATE_NR(element)])
15133         return MP_NO_ACTION;
15134     }
15135     else if (IS_EM_GATE_GRAY(element))
15136     {
15137       if (!player->key[EM_GATE_GRAY_NR(element)])
15138         return MP_NO_ACTION;
15139     }
15140     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15141     {
15142       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15143         return MP_NO_ACTION;
15144     }
15145     else if (IS_EMC_GATE(element))
15146     {
15147       if (!player->key[EMC_GATE_NR(element)])
15148         return MP_NO_ACTION;
15149     }
15150     else if (IS_EMC_GATE_GRAY(element))
15151     {
15152       if (!player->key[EMC_GATE_GRAY_NR(element)])
15153         return MP_NO_ACTION;
15154     }
15155     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15156     {
15157       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15158         return MP_NO_ACTION;
15159     }
15160     else if (element == EL_DC_GATE_WHITE ||
15161              element == EL_DC_GATE_WHITE_GRAY ||
15162              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15163     {
15164       if (player->num_white_keys == 0)
15165         return MP_NO_ACTION;
15166
15167       player->num_white_keys--;
15168     }
15169     else if (IS_SP_PORT(element))
15170     {
15171       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15172           element == EL_SP_GRAVITY_PORT_RIGHT ||
15173           element == EL_SP_GRAVITY_PORT_UP ||
15174           element == EL_SP_GRAVITY_PORT_DOWN)
15175 #if USE_PLAYER_GRAVITY
15176         player->gravity = !player->gravity;
15177 #else
15178         game.gravity = !game.gravity;
15179 #endif
15180       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15181                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15182                element == EL_SP_GRAVITY_ON_PORT_UP ||
15183                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15184 #if USE_PLAYER_GRAVITY
15185         player->gravity = TRUE;
15186 #else
15187         game.gravity = TRUE;
15188 #endif
15189       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15190                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15191                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15192                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15193 #if USE_PLAYER_GRAVITY
15194         player->gravity = FALSE;
15195 #else
15196         game.gravity = FALSE;
15197 #endif
15198     }
15199
15200     /* automatically move to the next field with double speed */
15201     player->programmed_action = move_direction;
15202
15203     if (player->move_delay_reset_counter == 0)
15204     {
15205       player->move_delay_reset_counter = 2;     /* two double speed steps */
15206
15207       DOUBLE_PLAYER_SPEED(player);
15208     }
15209
15210     PlayLevelSoundAction(x, y, ACTION_PASSING);
15211   }
15212   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15213   {
15214     RemoveField(x, y);
15215
15216     if (mode != DF_SNAP)
15217     {
15218       GfxElement[x][y] = GFX_ELEMENT(element);
15219       player->is_digging = TRUE;
15220     }
15221
15222     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15223
15224     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15225                                         player->index_bit, dig_side);
15226
15227     if (mode == DF_SNAP)
15228     {
15229 #if USE_NEW_SNAP_DELAY
15230       if (level.block_snap_field)
15231         setFieldForSnapping(x, y, element, move_direction);
15232       else
15233         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15234 #else
15235       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15236 #endif
15237
15238       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15239                                           player->index_bit, dig_side);
15240     }
15241   }
15242   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15243   {
15244     RemoveField(x, y);
15245
15246     if (is_player && mode != DF_SNAP)
15247     {
15248       GfxElement[x][y] = element;
15249       player->is_collecting = TRUE;
15250     }
15251
15252     if (element == EL_SPEED_PILL)
15253     {
15254       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15255     }
15256     else if (element == EL_EXTRA_TIME && level.time > 0)
15257     {
15258       TimeLeft += level.extra_time;
15259
15260 #if 1
15261       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15262
15263       DisplayGameControlValues();
15264 #else
15265       DrawGameValue_Time(TimeLeft);
15266 #endif
15267     }
15268     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15269     {
15270       player->shield_normal_time_left += level.shield_normal_time;
15271       if (element == EL_SHIELD_DEADLY)
15272         player->shield_deadly_time_left += level.shield_deadly_time;
15273     }
15274     else if (element == EL_DYNAMITE ||
15275              element == EL_EM_DYNAMITE ||
15276              element == EL_SP_DISK_RED)
15277     {
15278       if (player->inventory_size < MAX_INVENTORY_SIZE)
15279         player->inventory_element[player->inventory_size++] = element;
15280
15281       DrawGameDoorValues();
15282     }
15283     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15284     {
15285       player->dynabomb_count++;
15286       player->dynabombs_left++;
15287     }
15288     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15289     {
15290       player->dynabomb_size++;
15291     }
15292     else if (element == EL_DYNABOMB_INCREASE_POWER)
15293     {
15294       player->dynabomb_xl = TRUE;
15295     }
15296     else if (IS_KEY(element))
15297     {
15298       player->key[KEY_NR(element)] = TRUE;
15299
15300       DrawGameDoorValues();
15301     }
15302     else if (element == EL_DC_KEY_WHITE)
15303     {
15304       player->num_white_keys++;
15305
15306       /* display white keys? */
15307       /* DrawGameDoorValues(); */
15308     }
15309     else if (IS_ENVELOPE(element))
15310     {
15311       player->show_envelope = element;
15312     }
15313     else if (element == EL_EMC_LENSES)
15314     {
15315       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15316
15317       RedrawAllInvisibleElementsForLenses();
15318     }
15319     else if (element == EL_EMC_MAGNIFIER)
15320     {
15321       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15322
15323       RedrawAllInvisibleElementsForMagnifier();
15324     }
15325     else if (IS_DROPPABLE(element) ||
15326              IS_THROWABLE(element))     /* can be collected and dropped */
15327     {
15328       int i;
15329
15330       if (collect_count == 0)
15331         player->inventory_infinite_element = element;
15332       else
15333         for (i = 0; i < collect_count; i++)
15334           if (player->inventory_size < MAX_INVENTORY_SIZE)
15335             player->inventory_element[player->inventory_size++] = element;
15336
15337       DrawGameDoorValues();
15338     }
15339     else if (collect_count > 0)
15340     {
15341       local_player->gems_still_needed -= collect_count;
15342       if (local_player->gems_still_needed < 0)
15343         local_player->gems_still_needed = 0;
15344
15345 #if 1
15346       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15347
15348       DisplayGameControlValues();
15349 #else
15350       DrawGameValue_Emeralds(local_player->gems_still_needed);
15351 #endif
15352     }
15353
15354     RaiseScoreElement(element);
15355     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15356
15357     if (is_player)
15358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15359                                           player->index_bit, dig_side);
15360
15361     if (mode == DF_SNAP)
15362     {
15363 #if USE_NEW_SNAP_DELAY
15364       if (level.block_snap_field)
15365         setFieldForSnapping(x, y, element, move_direction);
15366       else
15367         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15368 #else
15369       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15370 #endif
15371
15372       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15373                                           player->index_bit, dig_side);
15374     }
15375   }
15376   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15377   {
15378     if (mode == DF_SNAP && element != EL_BD_ROCK)
15379       return MP_NO_ACTION;
15380
15381     if (CAN_FALL(element) && dy)
15382       return MP_NO_ACTION;
15383
15384     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15385         !(element == EL_SPRING && level.use_spring_bug))
15386       return MP_NO_ACTION;
15387
15388     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15389         ((move_direction & MV_VERTICAL &&
15390           ((element_info[element].move_pattern & MV_LEFT &&
15391             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15392            (element_info[element].move_pattern & MV_RIGHT &&
15393             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15394          (move_direction & MV_HORIZONTAL &&
15395           ((element_info[element].move_pattern & MV_UP &&
15396             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15397            (element_info[element].move_pattern & MV_DOWN &&
15398             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15399       return MP_NO_ACTION;
15400
15401     /* do not push elements already moving away faster than player */
15402     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15403         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15404       return MP_NO_ACTION;
15405
15406     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15407     {
15408       if (player->push_delay_value == -1 || !player_was_pushing)
15409         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15410     }
15411     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15412     {
15413       if (player->push_delay_value == -1)
15414         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15415     }
15416     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15417     {
15418       if (!player->is_pushing)
15419         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15420     }
15421
15422     player->is_pushing = TRUE;
15423     player->is_active = TRUE;
15424
15425     if (!(IN_LEV_FIELD(nextx, nexty) &&
15426           (IS_FREE(nextx, nexty) ||
15427            (IS_SB_ELEMENT(element) &&
15428             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15429            (IS_CUSTOM_ELEMENT(element) &&
15430             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15431       return MP_NO_ACTION;
15432
15433     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15434       return MP_NO_ACTION;
15435
15436     if (player->push_delay == -1)       /* new pushing; restart delay */
15437       player->push_delay = 0;
15438
15439     if (player->push_delay < player->push_delay_value &&
15440         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15441         element != EL_SPRING && element != EL_BALLOON)
15442     {
15443       /* make sure that there is no move delay before next try to push */
15444       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15445         player->move_delay = 0;
15446
15447       return MP_NO_ACTION;
15448     }
15449
15450     if (IS_CUSTOM_ELEMENT(element) &&
15451         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15452     {
15453       if (!DigFieldByCE(nextx, nexty, element))
15454         return MP_NO_ACTION;
15455     }
15456
15457     if (IS_SB_ELEMENT(element))
15458     {
15459       if (element == EL_SOKOBAN_FIELD_FULL)
15460       {
15461         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15462         local_player->sokobanfields_still_needed++;
15463       }
15464
15465       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15466       {
15467         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15468         local_player->sokobanfields_still_needed--;
15469       }
15470
15471       Feld[x][y] = EL_SOKOBAN_OBJECT;
15472
15473       if (Back[x][y] == Back[nextx][nexty])
15474         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15475       else if (Back[x][y] != 0)
15476         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15477                                     ACTION_EMPTYING);
15478       else
15479         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15480                                     ACTION_FILLING);
15481
15482 #if 1
15483       if (local_player->sokobanfields_still_needed == 0 &&
15484           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15485 #else
15486       if (local_player->sokobanfields_still_needed == 0 &&
15487           game.emulation == EMU_SOKOBAN)
15488 #endif
15489       {
15490         PlayerWins(player);
15491
15492         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15493       }
15494     }
15495     else
15496       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15497
15498     InitMovingField(x, y, move_direction);
15499     GfxAction[x][y] = ACTION_PUSHING;
15500
15501     if (mode == DF_SNAP)
15502       ContinueMoving(x, y);
15503     else
15504       MovPos[x][y] = (dx != 0 ? dx : dy);
15505
15506     Pushed[x][y] = TRUE;
15507     Pushed[nextx][nexty] = TRUE;
15508
15509     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15510       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15511     else
15512       player->push_delay_value = -1;    /* get new value later */
15513
15514     /* check for element change _after_ element has been pushed */
15515     if (game.use_change_when_pushing_bug)
15516     {
15517       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15518                                  player->index_bit, dig_side);
15519       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15520                                           player->index_bit, dig_side);
15521     }
15522   }
15523   else if (IS_SWITCHABLE(element))
15524   {
15525     if (PLAYER_SWITCHING(player, x, y))
15526     {
15527       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15528                                           player->index_bit, dig_side);
15529
15530       return MP_ACTION;
15531     }
15532
15533     player->is_switching = TRUE;
15534     player->switch_x = x;
15535     player->switch_y = y;
15536
15537     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15538
15539     if (element == EL_ROBOT_WHEEL)
15540     {
15541       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15542       ZX = x;
15543       ZY = y;
15544
15545       game.robot_wheel_active = TRUE;
15546
15547       TEST_DrawLevelField(x, y);
15548     }
15549     else if (element == EL_SP_TERMINAL)
15550     {
15551       int xx, yy;
15552
15553       SCAN_PLAYFIELD(xx, yy)
15554       {
15555         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15556           Bang(xx, yy);
15557         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15558           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15559       }
15560     }
15561     else if (IS_BELT_SWITCH(element))
15562     {
15563       ToggleBeltSwitch(x, y);
15564     }
15565     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15566              element == EL_SWITCHGATE_SWITCH_DOWN ||
15567              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15568              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15569     {
15570       ToggleSwitchgateSwitch(x, y);
15571     }
15572     else if (element == EL_LIGHT_SWITCH ||
15573              element == EL_LIGHT_SWITCH_ACTIVE)
15574     {
15575       ToggleLightSwitch(x, y);
15576     }
15577     else if (element == EL_TIMEGATE_SWITCH ||
15578              element == EL_DC_TIMEGATE_SWITCH)
15579     {
15580       ActivateTimegateSwitch(x, y);
15581     }
15582     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15583              element == EL_BALLOON_SWITCH_RIGHT ||
15584              element == EL_BALLOON_SWITCH_UP    ||
15585              element == EL_BALLOON_SWITCH_DOWN  ||
15586              element == EL_BALLOON_SWITCH_NONE  ||
15587              element == EL_BALLOON_SWITCH_ANY)
15588     {
15589       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15590                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15591                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15592                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15593                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15594                              move_direction);
15595     }
15596     else if (element == EL_LAMP)
15597     {
15598       Feld[x][y] = EL_LAMP_ACTIVE;
15599       local_player->lights_still_needed--;
15600
15601       ResetGfxAnimation(x, y);
15602       TEST_DrawLevelField(x, y);
15603     }
15604     else if (element == EL_TIME_ORB_FULL)
15605     {
15606       Feld[x][y] = EL_TIME_ORB_EMPTY;
15607
15608       if (level.time > 0 || level.use_time_orb_bug)
15609       {
15610         TimeLeft += level.time_orb_time;
15611
15612 #if 1
15613         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15614
15615         DisplayGameControlValues();
15616 #else
15617         DrawGameValue_Time(TimeLeft);
15618 #endif
15619       }
15620
15621       ResetGfxAnimation(x, y);
15622       TEST_DrawLevelField(x, y);
15623     }
15624     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15625              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15626     {
15627       int xx, yy;
15628
15629       game.ball_state = !game.ball_state;
15630
15631       SCAN_PLAYFIELD(xx, yy)
15632       {
15633         int e = Feld[xx][yy];
15634
15635         if (game.ball_state)
15636         {
15637           if (e == EL_EMC_MAGIC_BALL)
15638             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15639           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15640             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15641         }
15642         else
15643         {
15644           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15645             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15646           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15647             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15648         }
15649       }
15650     }
15651
15652     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15653                                         player->index_bit, dig_side);
15654
15655     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15656                                         player->index_bit, dig_side);
15657
15658     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15659                                         player->index_bit, dig_side);
15660
15661     return MP_ACTION;
15662   }
15663   else
15664   {
15665     if (!PLAYER_SWITCHING(player, x, y))
15666     {
15667       player->is_switching = TRUE;
15668       player->switch_x = x;
15669       player->switch_y = y;
15670
15671       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15672                                  player->index_bit, dig_side);
15673       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15674                                           player->index_bit, dig_side);
15675
15676       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15677                                  player->index_bit, dig_side);
15678       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15679                                           player->index_bit, dig_side);
15680     }
15681
15682     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15683                                player->index_bit, dig_side);
15684     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15685                                         player->index_bit, dig_side);
15686
15687     return MP_NO_ACTION;
15688   }
15689
15690   player->push_delay = -1;
15691
15692   if (is_player)                /* function can also be called by EL_PENGUIN */
15693   {
15694     if (Feld[x][y] != element)          /* really digged/collected something */
15695     {
15696       player->is_collecting = !player->is_digging;
15697       player->is_active = TRUE;
15698     }
15699   }
15700
15701   return MP_MOVING;
15702 }
15703
15704 static boolean DigFieldByCE(int x, int y, int digging_element)
15705 {
15706   int element = Feld[x][y];
15707
15708   if (!IS_FREE(x, y))
15709   {
15710     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15711                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15712                   ACTION_BREAKING);
15713
15714     /* no element can dig solid indestructible elements */
15715     if (IS_INDESTRUCTIBLE(element) &&
15716         !IS_DIGGABLE(element) &&
15717         !IS_COLLECTIBLE(element))
15718       return FALSE;
15719
15720     if (AmoebaNr[x][y] &&
15721         (element == EL_AMOEBA_FULL ||
15722          element == EL_BD_AMOEBA ||
15723          element == EL_AMOEBA_GROWING))
15724     {
15725       AmoebaCnt[AmoebaNr[x][y]]--;
15726       AmoebaCnt2[AmoebaNr[x][y]]--;
15727     }
15728
15729     if (IS_MOVING(x, y))
15730       RemoveMovingField(x, y);
15731     else
15732     {
15733       RemoveField(x, y);
15734       TEST_DrawLevelField(x, y);
15735     }
15736
15737     /* if digged element was about to explode, prevent the explosion */
15738     ExplodeField[x][y] = EX_TYPE_NONE;
15739
15740     PlayLevelSoundAction(x, y, action);
15741   }
15742
15743   Store[x][y] = EL_EMPTY;
15744
15745 #if 1
15746   /* this makes it possible to leave the removed element again */
15747   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15748     Store[x][y] = element;
15749 #else
15750   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15751   {
15752     int move_leave_element = element_info[digging_element].move_leave_element;
15753
15754     /* this makes it possible to leave the removed element again */
15755     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15756                    element : move_leave_element);
15757   }
15758 #endif
15759
15760   return TRUE;
15761 }
15762
15763 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15764 {
15765   int jx = player->jx, jy = player->jy;
15766   int x = jx + dx, y = jy + dy;
15767   int snap_direction = (dx == -1 ? MV_LEFT  :
15768                         dx == +1 ? MV_RIGHT :
15769                         dy == -1 ? MV_UP    :
15770                         dy == +1 ? MV_DOWN  : MV_NONE);
15771   boolean can_continue_snapping = (level.continuous_snapping &&
15772                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15773
15774   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15775     return FALSE;
15776
15777   if (!player->active || !IN_LEV_FIELD(x, y))
15778     return FALSE;
15779
15780   if (dx && dy)
15781     return FALSE;
15782
15783   if (!dx && !dy)
15784   {
15785     if (player->MovPos == 0)
15786       player->is_pushing = FALSE;
15787
15788     player->is_snapping = FALSE;
15789
15790     if (player->MovPos == 0)
15791     {
15792       player->is_moving = FALSE;
15793       player->is_digging = FALSE;
15794       player->is_collecting = FALSE;
15795     }
15796
15797     return FALSE;
15798   }
15799
15800 #if USE_NEW_CONTINUOUS_SNAPPING
15801   /* prevent snapping with already pressed snap key when not allowed */
15802   if (player->is_snapping && !can_continue_snapping)
15803     return FALSE;
15804 #else
15805   if (player->is_snapping)
15806     return FALSE;
15807 #endif
15808
15809   player->MovDir = snap_direction;
15810
15811   if (player->MovPos == 0)
15812   {
15813     player->is_moving = FALSE;
15814     player->is_digging = FALSE;
15815     player->is_collecting = FALSE;
15816   }
15817
15818   player->is_dropping = FALSE;
15819   player->is_dropping_pressed = FALSE;
15820   player->drop_pressed_delay = 0;
15821
15822   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15823     return FALSE;
15824
15825   player->is_snapping = TRUE;
15826   player->is_active = TRUE;
15827
15828   if (player->MovPos == 0)
15829   {
15830     player->is_moving = FALSE;
15831     player->is_digging = FALSE;
15832     player->is_collecting = FALSE;
15833   }
15834
15835   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15836     TEST_DrawLevelField(player->last_jx, player->last_jy);
15837
15838   TEST_DrawLevelField(x, y);
15839
15840   return TRUE;
15841 }
15842
15843 static boolean DropElement(struct PlayerInfo *player)
15844 {
15845   int old_element, new_element;
15846   int dropx = player->jx, dropy = player->jy;
15847   int drop_direction = player->MovDir;
15848   int drop_side = drop_direction;
15849 #if 1
15850   int drop_element = get_next_dropped_element(player);
15851 #else
15852   int drop_element = (player->inventory_size > 0 ?
15853                       player->inventory_element[player->inventory_size - 1] :
15854                       player->inventory_infinite_element != EL_UNDEFINED ?
15855                       player->inventory_infinite_element :
15856                       player->dynabombs_left > 0 ?
15857                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15858                       EL_UNDEFINED);
15859 #endif
15860
15861   player->is_dropping_pressed = TRUE;
15862
15863   /* do not drop an element on top of another element; when holding drop key
15864      pressed without moving, dropped element must move away before the next
15865      element can be dropped (this is especially important if the next element
15866      is dynamite, which can be placed on background for historical reasons) */
15867   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15868     return MP_ACTION;
15869
15870   if (IS_THROWABLE(drop_element))
15871   {
15872     dropx += GET_DX_FROM_DIR(drop_direction);
15873     dropy += GET_DY_FROM_DIR(drop_direction);
15874
15875     if (!IN_LEV_FIELD(dropx, dropy))
15876       return FALSE;
15877   }
15878
15879   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15880   new_element = drop_element;           /* default: no change when dropping */
15881
15882   /* check if player is active, not moving and ready to drop */
15883   if (!player->active || player->MovPos || player->drop_delay > 0)
15884     return FALSE;
15885
15886   /* check if player has anything that can be dropped */
15887   if (new_element == EL_UNDEFINED)
15888     return FALSE;
15889
15890   /* check if drop key was pressed long enough for EM style dynamite */
15891   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15892     return FALSE;
15893
15894   /* check if anything can be dropped at the current position */
15895   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15896     return FALSE;
15897
15898   /* collected custom elements can only be dropped on empty fields */
15899   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15900     return FALSE;
15901
15902   if (old_element != EL_EMPTY)
15903     Back[dropx][dropy] = old_element;   /* store old element on this field */
15904
15905   ResetGfxAnimation(dropx, dropy);
15906   ResetRandomAnimationValue(dropx, dropy);
15907
15908   if (player->inventory_size > 0 ||
15909       player->inventory_infinite_element != EL_UNDEFINED)
15910   {
15911     if (player->inventory_size > 0)
15912     {
15913       player->inventory_size--;
15914
15915       DrawGameDoorValues();
15916
15917       if (new_element == EL_DYNAMITE)
15918         new_element = EL_DYNAMITE_ACTIVE;
15919       else if (new_element == EL_EM_DYNAMITE)
15920         new_element = EL_EM_DYNAMITE_ACTIVE;
15921       else if (new_element == EL_SP_DISK_RED)
15922         new_element = EL_SP_DISK_RED_ACTIVE;
15923     }
15924
15925     Feld[dropx][dropy] = new_element;
15926
15927     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15928       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15929                           el2img(Feld[dropx][dropy]), 0);
15930
15931     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15932
15933     /* needed if previous element just changed to "empty" in the last frame */
15934     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15935
15936     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15937                                player->index_bit, drop_side);
15938     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15939                                         CE_PLAYER_DROPS_X,
15940                                         player->index_bit, drop_side);
15941
15942     TestIfElementTouchesCustomElement(dropx, dropy);
15943   }
15944   else          /* player is dropping a dyna bomb */
15945   {
15946     player->dynabombs_left--;
15947
15948     Feld[dropx][dropy] = new_element;
15949
15950     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15951       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15952                           el2img(Feld[dropx][dropy]), 0);
15953
15954     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15955   }
15956
15957   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15958     InitField_WithBug1(dropx, dropy, FALSE);
15959
15960   new_element = Feld[dropx][dropy];     /* element might have changed */
15961
15962   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15963       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15964   {
15965     int move_direction, nextx, nexty;
15966
15967     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15968       MovDir[dropx][dropy] = drop_direction;
15969
15970     move_direction = MovDir[dropx][dropy];
15971     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15972     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15973
15974     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15975
15976 #if USE_FIX_IMPACT_COLLISION
15977     /* do not cause impact style collision by dropping elements that can fall */
15978     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15979 #else
15980     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15981 #endif
15982   }
15983
15984   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15985   player->is_dropping = TRUE;
15986
15987   player->drop_pressed_delay = 0;
15988   player->is_dropping_pressed = FALSE;
15989
15990   player->drop_x = dropx;
15991   player->drop_y = dropy;
15992
15993   return TRUE;
15994 }
15995
15996 /* ------------------------------------------------------------------------- */
15997 /* game sound playing functions                                              */
15998 /* ------------------------------------------------------------------------- */
15999
16000 static int *loop_sound_frame = NULL;
16001 static int *loop_sound_volume = NULL;
16002
16003 void InitPlayLevelSound()
16004 {
16005   int num_sounds = getSoundListSize();
16006
16007   checked_free(loop_sound_frame);
16008   checked_free(loop_sound_volume);
16009
16010   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16011   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16012 }
16013
16014 static void PlayLevelSound(int x, int y, int nr)
16015 {
16016   int sx = SCREENX(x), sy = SCREENY(y);
16017   int volume, stereo_position;
16018   int max_distance = 8;
16019   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16020
16021   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16022       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16023     return;
16024
16025   if (!IN_LEV_FIELD(x, y) ||
16026       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16027       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16028     return;
16029
16030   volume = SOUND_MAX_VOLUME;
16031
16032   if (!IN_SCR_FIELD(sx, sy))
16033   {
16034     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16035     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16036
16037     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16038   }
16039
16040   stereo_position = (SOUND_MAX_LEFT +
16041                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16042                      (SCR_FIELDX + 2 * max_distance));
16043
16044   if (IS_LOOP_SOUND(nr))
16045   {
16046     /* This assures that quieter loop sounds do not overwrite louder ones,
16047        while restarting sound volume comparison with each new game frame. */
16048
16049     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16050       return;
16051
16052     loop_sound_volume[nr] = volume;
16053     loop_sound_frame[nr] = FrameCounter;
16054   }
16055
16056   PlaySoundExt(nr, volume, stereo_position, type);
16057 }
16058
16059 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16060 {
16061   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16062                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16063                  y < LEVELY(BY1) ? LEVELY(BY1) :
16064                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16065                  sound_action);
16066 }
16067
16068 static void PlayLevelSoundAction(int x, int y, int action)
16069 {
16070   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16071 }
16072
16073 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16074 {
16075   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16076
16077   if (sound_effect != SND_UNDEFINED)
16078     PlayLevelSound(x, y, sound_effect);
16079 }
16080
16081 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16082                                               int action)
16083 {
16084   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16085
16086   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16087     PlayLevelSound(x, y, sound_effect);
16088 }
16089
16090 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16091 {
16092   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16093
16094   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16095     PlayLevelSound(x, y, sound_effect);
16096 }
16097
16098 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16099 {
16100   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16101
16102   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16103     StopSound(sound_effect);
16104 }
16105
16106 static void PlayLevelMusic()
16107 {
16108   if (levelset.music[level_nr] != MUS_UNDEFINED)
16109     PlayMusic(levelset.music[level_nr]);        /* from config file */
16110   else
16111     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16112 }
16113
16114 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16115 {
16116   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16117   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16118   int x = xx - 1 - offset;
16119   int y = yy - 1 - offset;
16120
16121   switch (sample)
16122   {
16123     case SAMPLE_blank:
16124       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16125       break;
16126
16127     case SAMPLE_roll:
16128       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16129       break;
16130
16131     case SAMPLE_stone:
16132       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16133       break;
16134
16135     case SAMPLE_nut:
16136       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16137       break;
16138
16139     case SAMPLE_crack:
16140       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16141       break;
16142
16143     case SAMPLE_bug:
16144       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16145       break;
16146
16147     case SAMPLE_tank:
16148       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16149       break;
16150
16151     case SAMPLE_android_clone:
16152       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16153       break;
16154
16155     case SAMPLE_android_move:
16156       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16157       break;
16158
16159     case SAMPLE_spring:
16160       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16161       break;
16162
16163     case SAMPLE_slurp:
16164       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16165       break;
16166
16167     case SAMPLE_eater:
16168       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16169       break;
16170
16171     case SAMPLE_eater_eat:
16172       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16173       break;
16174
16175     case SAMPLE_alien:
16176       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16177       break;
16178
16179     case SAMPLE_collect:
16180       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16181       break;
16182
16183     case SAMPLE_diamond:
16184       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16185       break;
16186
16187     case SAMPLE_squash:
16188       /* !!! CHECK THIS !!! */
16189 #if 1
16190       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16191 #else
16192       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16193 #endif
16194       break;
16195
16196     case SAMPLE_wonderfall:
16197       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16198       break;
16199
16200     case SAMPLE_drip:
16201       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16202       break;
16203
16204     case SAMPLE_push:
16205       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16206       break;
16207
16208     case SAMPLE_dirt:
16209       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16210       break;
16211
16212     case SAMPLE_acid:
16213       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16214       break;
16215
16216     case SAMPLE_ball:
16217       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16218       break;
16219
16220     case SAMPLE_grow:
16221       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16222       break;
16223
16224     case SAMPLE_wonder:
16225       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16226       break;
16227
16228     case SAMPLE_door:
16229       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16230       break;
16231
16232     case SAMPLE_exit_open:
16233       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16234       break;
16235
16236     case SAMPLE_exit_leave:
16237       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16238       break;
16239
16240     case SAMPLE_dynamite:
16241       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16242       break;
16243
16244     case SAMPLE_tick:
16245       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16246       break;
16247
16248     case SAMPLE_press:
16249       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16250       break;
16251
16252     case SAMPLE_wheel:
16253       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16254       break;
16255
16256     case SAMPLE_boom:
16257       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16258       break;
16259
16260     case SAMPLE_die:
16261       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16262       break;
16263
16264     case SAMPLE_time:
16265       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16266       break;
16267
16268     default:
16269       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16270       break;
16271   }
16272 }
16273
16274 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16275 {
16276   int element = map_element_SP_to_RND(element_sp);
16277   int action = map_action_SP_to_RND(action_sp);
16278   int offset = (setup.sp_show_border_elements ? 0 : 1);
16279   int x = xx - offset;
16280   int y = yy - offset;
16281
16282 #if 0
16283   printf("::: %d -> %d\n", element_sp, action_sp);
16284 #endif
16285
16286   PlayLevelSoundElementAction(x, y, element, action);
16287 }
16288
16289 #if 0
16290 void ChangeTime(int value)
16291 {
16292   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16293
16294   *time += value;
16295
16296   /* EMC game engine uses value from time counter of RND game engine */
16297   level.native_em_level->lev->time = *time;
16298
16299   DrawGameValue_Time(*time);
16300 }
16301
16302 void RaiseScore(int value)
16303 {
16304   /* EMC game engine and RND game engine have separate score counters */
16305   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16306                 &level.native_em_level->lev->score : &local_player->score);
16307
16308   *score += value;
16309
16310   DrawGameValue_Score(*score);
16311 }
16312 #endif
16313
16314 void RaiseScore(int value)
16315 {
16316   local_player->score += value;
16317
16318 #if 1
16319   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16320
16321   DisplayGameControlValues();
16322 #else
16323   DrawGameValue_Score(local_player->score);
16324 #endif
16325 }
16326
16327 void RaiseScoreElement(int element)
16328 {
16329   switch (element)
16330   {
16331     case EL_EMERALD:
16332     case EL_BD_DIAMOND:
16333     case EL_EMERALD_YELLOW:
16334     case EL_EMERALD_RED:
16335     case EL_EMERALD_PURPLE:
16336     case EL_SP_INFOTRON:
16337       RaiseScore(level.score[SC_EMERALD]);
16338       break;
16339     case EL_DIAMOND:
16340       RaiseScore(level.score[SC_DIAMOND]);
16341       break;
16342     case EL_CRYSTAL:
16343       RaiseScore(level.score[SC_CRYSTAL]);
16344       break;
16345     case EL_PEARL:
16346       RaiseScore(level.score[SC_PEARL]);
16347       break;
16348     case EL_BUG:
16349     case EL_BD_BUTTERFLY:
16350     case EL_SP_ELECTRON:
16351       RaiseScore(level.score[SC_BUG]);
16352       break;
16353     case EL_SPACESHIP:
16354     case EL_BD_FIREFLY:
16355     case EL_SP_SNIKSNAK:
16356       RaiseScore(level.score[SC_SPACESHIP]);
16357       break;
16358     case EL_YAMYAM:
16359     case EL_DARK_YAMYAM:
16360       RaiseScore(level.score[SC_YAMYAM]);
16361       break;
16362     case EL_ROBOT:
16363       RaiseScore(level.score[SC_ROBOT]);
16364       break;
16365     case EL_PACMAN:
16366       RaiseScore(level.score[SC_PACMAN]);
16367       break;
16368     case EL_NUT:
16369       RaiseScore(level.score[SC_NUT]);
16370       break;
16371     case EL_DYNAMITE:
16372     case EL_EM_DYNAMITE:
16373     case EL_SP_DISK_RED:
16374     case EL_DYNABOMB_INCREASE_NUMBER:
16375     case EL_DYNABOMB_INCREASE_SIZE:
16376     case EL_DYNABOMB_INCREASE_POWER:
16377       RaiseScore(level.score[SC_DYNAMITE]);
16378       break;
16379     case EL_SHIELD_NORMAL:
16380     case EL_SHIELD_DEADLY:
16381       RaiseScore(level.score[SC_SHIELD]);
16382       break;
16383     case EL_EXTRA_TIME:
16384       RaiseScore(level.extra_time_score);
16385       break;
16386     case EL_KEY_1:
16387     case EL_KEY_2:
16388     case EL_KEY_3:
16389     case EL_KEY_4:
16390     case EL_EM_KEY_1:
16391     case EL_EM_KEY_2:
16392     case EL_EM_KEY_3:
16393     case EL_EM_KEY_4:
16394     case EL_EMC_KEY_5:
16395     case EL_EMC_KEY_6:
16396     case EL_EMC_KEY_7:
16397     case EL_EMC_KEY_8:
16398     case EL_DC_KEY_WHITE:
16399       RaiseScore(level.score[SC_KEY]);
16400       break;
16401     default:
16402       RaiseScore(element_info[element].collect_score);
16403       break;
16404   }
16405 }
16406
16407 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16408 {
16409   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16410   {
16411 #if defined(NETWORK_AVALIABLE)
16412     if (options.network)
16413       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16414     else
16415 #endif
16416     {
16417       if (quick_quit)
16418       {
16419 #if 1
16420
16421 #if 1
16422         FadeSkipNextFadeIn();
16423 #else
16424         fading = fading_none;
16425 #endif
16426
16427 #else
16428         OpenDoor(DOOR_CLOSE_1);
16429 #endif
16430
16431         game_status = GAME_MODE_MAIN;
16432
16433 #if 1
16434         DrawAndFadeInMainMenu(REDRAW_FIELD);
16435 #else
16436         DrawMainMenu();
16437 #endif
16438       }
16439       else
16440       {
16441 #if 0
16442         FadeOut(REDRAW_FIELD);
16443 #endif
16444
16445         game_status = GAME_MODE_MAIN;
16446
16447         DrawAndFadeInMainMenu(REDRAW_FIELD);
16448       }
16449     }
16450   }
16451   else          /* continue playing the game */
16452   {
16453     if (tape.playing && tape.deactivate_display)
16454       TapeDeactivateDisplayOff(TRUE);
16455
16456     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16457
16458     if (tape.playing && tape.deactivate_display)
16459       TapeDeactivateDisplayOn();
16460   }
16461 }
16462
16463 void RequestQuitGame(boolean ask_if_really_quit)
16464 {
16465   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16466   boolean skip_request = AllPlayersGone || quick_quit;
16467
16468   RequestQuitGameExt(skip_request, quick_quit,
16469                      "Do you really want to quit the game ?");
16470 }
16471
16472
16473 /* ------------------------------------------------------------------------- */
16474 /* random generator functions                                                */
16475 /* ------------------------------------------------------------------------- */
16476
16477 unsigned int InitEngineRandom_RND(long seed)
16478 {
16479   game.num_random_calls = 0;
16480
16481 #if 0
16482   unsigned int rnd_seed = InitEngineRandom(seed);
16483
16484   printf("::: START RND: %d\n", rnd_seed);
16485
16486   return rnd_seed;
16487 #else
16488
16489   return InitEngineRandom(seed);
16490
16491 #endif
16492
16493 }
16494
16495 unsigned int RND(int max)
16496 {
16497   if (max > 0)
16498   {
16499     game.num_random_calls++;
16500
16501     return GetEngineRandom(max);
16502   }
16503
16504   return 0;
16505 }
16506
16507
16508 /* ------------------------------------------------------------------------- */
16509 /* game engine snapshot handling functions                                   */
16510 /* ------------------------------------------------------------------------- */
16511
16512 struct EngineSnapshotInfo
16513 {
16514   /* runtime values for custom element collect score */
16515   int collect_score[NUM_CUSTOM_ELEMENTS];
16516
16517   /* runtime values for group element choice position */
16518   int choice_pos[NUM_GROUP_ELEMENTS];
16519
16520   /* runtime values for belt position animations */
16521   int belt_graphic[4][NUM_BELT_PARTS];
16522   int belt_anim_mode[4][NUM_BELT_PARTS];
16523 };
16524
16525 static struct EngineSnapshotInfo engine_snapshot_rnd;
16526 static char *snapshot_level_identifier = NULL;
16527 static int snapshot_level_nr = -1;
16528
16529 static void SaveEngineSnapshotValues_RND()
16530 {
16531   static int belt_base_active_element[4] =
16532   {
16533     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16534     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16535     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16536     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16537   };
16538   int i, j;
16539
16540   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16541   {
16542     int element = EL_CUSTOM_START + i;
16543
16544     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16545   }
16546
16547   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16548   {
16549     int element = EL_GROUP_START + i;
16550
16551     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16552   }
16553
16554   for (i = 0; i < 4; i++)
16555   {
16556     for (j = 0; j < NUM_BELT_PARTS; j++)
16557     {
16558       int element = belt_base_active_element[i] + j;
16559       int graphic = el2img(element);
16560       int anim_mode = graphic_info[graphic].anim_mode;
16561
16562       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16563       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16564     }
16565   }
16566 }
16567
16568 static void LoadEngineSnapshotValues_RND()
16569 {
16570   unsigned long num_random_calls = game.num_random_calls;
16571   int i, j;
16572
16573   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16574   {
16575     int element = EL_CUSTOM_START + i;
16576
16577     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16578   }
16579
16580   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16581   {
16582     int element = EL_GROUP_START + i;
16583
16584     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16585   }
16586
16587   for (i = 0; i < 4; i++)
16588   {
16589     for (j = 0; j < NUM_BELT_PARTS; j++)
16590     {
16591       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16592       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16593
16594       graphic_info[graphic].anim_mode = anim_mode;
16595     }
16596   }
16597
16598   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16599   {
16600     InitRND(tape.random_seed);
16601     for (i = 0; i < num_random_calls; i++)
16602       RND(1);
16603   }
16604
16605   if (game.num_random_calls != num_random_calls)
16606   {
16607     Error(ERR_INFO, "number of random calls out of sync");
16608     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16609     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16610     Error(ERR_EXIT, "this should not happen -- please debug");
16611   }
16612 }
16613
16614 void SaveEngineSnapshot()
16615 {
16616   /* do not save snapshots from editor */
16617   if (level_editor_test_game)
16618     return;
16619
16620   /* free previous snapshot buffers, if needed */
16621   FreeEngineSnapshotBuffers();
16622
16623   /* copy some special values to a structure better suited for the snapshot */
16624
16625   SaveEngineSnapshotValues_RND();
16626   SaveEngineSnapshotValues_EM();
16627   SaveEngineSnapshotValues_SP();
16628
16629   /* save values stored in special snapshot structure */
16630
16631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16634
16635   /* save further RND engine values */
16636
16637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16640
16641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16645
16646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16651
16652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16655
16656   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16657
16658   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16659
16660   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16661   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16662
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16665   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16666   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16667   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16672   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16681
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16684
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16688
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16691
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16694   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16697
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16700
16701   /* save level identification information */
16702
16703   setString(&snapshot_level_identifier, leveldir_current->identifier);
16704   snapshot_level_nr = level_nr;
16705
16706 #if 0
16707   ListNode *node = engine_snapshot_list_rnd;
16708   int num_bytes = 0;
16709
16710   while (node != NULL)
16711   {
16712     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16713
16714     node = node->next;
16715   }
16716
16717   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16718 #endif
16719 }
16720
16721 void LoadEngineSnapshot()
16722 {
16723   /* restore generically stored snapshot buffers */
16724
16725   LoadEngineSnapshotBuffers();
16726
16727   /* restore special values from snapshot structure */
16728
16729   LoadEngineSnapshotValues_RND();
16730   LoadEngineSnapshotValues_EM();
16731   LoadEngineSnapshotValues_SP();
16732 }
16733
16734 boolean CheckEngineSnapshot()
16735 {
16736   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16737           snapshot_level_nr == level_nr);
16738 }
16739
16740
16741 /* ---------- new game button stuff ---------------------------------------- */
16742
16743 static struct
16744 {
16745   int graphic;
16746   struct Rect *pos;
16747   int gadget_id;
16748   char *infotext;
16749 } gamebutton_info[NUM_GAME_BUTTONS] =
16750 {
16751   {
16752     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16753     GAME_CTRL_ID_STOP,                  "stop game"
16754   },
16755   {
16756     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16757     GAME_CTRL_ID_PAUSE,                 "pause game"
16758   },
16759   {
16760     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16761     GAME_CTRL_ID_PLAY,                  "play game"
16762   },
16763   {
16764     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16765     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16766   },
16767   {
16768     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16769     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16770   },
16771   {
16772     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16773     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16774   }
16775 };
16776
16777 void CreateGameButtons()
16778 {
16779   int i;
16780
16781   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16782   {
16783     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16784     struct Rect *pos = gamebutton_info[i].pos;
16785     struct GadgetInfo *gi;
16786     int button_type;
16787     boolean checked;
16788     unsigned long event_mask;
16789     int gd_x   = gfx->src_x;
16790     int gd_y   = gfx->src_y;
16791     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16792     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16793     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16794     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16795     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16796     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16797     int id = i;
16798
16799     if (id == GAME_CTRL_ID_STOP ||
16800         id == GAME_CTRL_ID_PAUSE ||
16801         id == GAME_CTRL_ID_PLAY)
16802     {
16803       button_type = GD_TYPE_NORMAL_BUTTON;
16804       checked = FALSE;
16805       event_mask = GD_EVENT_RELEASED;
16806     }
16807     else
16808     {
16809       button_type = GD_TYPE_CHECK_BUTTON;
16810       checked =
16811         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16812          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16813          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16814       event_mask = GD_EVENT_PRESSED;
16815     }
16816
16817     gi = CreateGadget(GDI_CUSTOM_ID, id,
16818                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16819                       GDI_X, DX + pos->x,
16820                       GDI_Y, DY + pos->y,
16821                       GDI_WIDTH, gfx->width,
16822                       GDI_HEIGHT, gfx->height,
16823                       GDI_TYPE, button_type,
16824                       GDI_STATE, GD_BUTTON_UNPRESSED,
16825                       GDI_CHECKED, checked,
16826                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16827                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16828                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16829                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16830                       GDI_DIRECT_DRAW, FALSE,
16831                       GDI_EVENT_MASK, event_mask,
16832                       GDI_CALLBACK_ACTION, HandleGameButtons,
16833                       GDI_END);
16834
16835     if (gi == NULL)
16836       Error(ERR_EXIT, "cannot create gadget");
16837
16838     game_gadget[id] = gi;
16839   }
16840 }
16841
16842 void FreeGameButtons()
16843 {
16844   int i;
16845
16846   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16847     FreeGadget(game_gadget[i]);
16848 }
16849
16850 static void MapGameButtons()
16851 {
16852   int i;
16853
16854   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16855     MapGadget(game_gadget[i]);
16856 }
16857
16858 void UnmapGameButtons()
16859 {
16860   int i;
16861
16862   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16863     UnmapGadget(game_gadget[i]);
16864 }
16865
16866 void RedrawGameButtons()
16867 {
16868   int i;
16869
16870   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16871     RedrawGadget(game_gadget[i]);
16872 }
16873
16874 static void HandleGameButtonsExt(int id)
16875 {
16876   if (game_status != GAME_MODE_PLAYING)
16877     return;
16878
16879   switch (id)
16880   {
16881     case GAME_CTRL_ID_STOP:
16882       if (tape.playing)
16883         TapeStop();
16884       else
16885         RequestQuitGame(TRUE);
16886       break;
16887
16888     case GAME_CTRL_ID_PAUSE:
16889       if (options.network)
16890       {
16891 #if defined(NETWORK_AVALIABLE)
16892         if (tape.pausing)
16893           SendToServer_ContinuePlaying();
16894         else
16895           SendToServer_PausePlaying();
16896 #endif
16897       }
16898       else
16899         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16900       break;
16901
16902     case GAME_CTRL_ID_PLAY:
16903       if (tape.pausing)
16904       {
16905 #if defined(NETWORK_AVALIABLE)
16906         if (options.network)
16907           SendToServer_ContinuePlaying();
16908         else
16909 #endif
16910         {
16911           tape.pausing = FALSE;
16912           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16913         }
16914       }
16915       break;
16916
16917     case SOUND_CTRL_ID_MUSIC:
16918       if (setup.sound_music)
16919       { 
16920         setup.sound_music = FALSE;
16921
16922         FadeMusic();
16923       }
16924       else if (audio.music_available)
16925       { 
16926         setup.sound = setup.sound_music = TRUE;
16927
16928         SetAudioMode(setup.sound);
16929
16930         PlayLevelMusic();
16931       }
16932       break;
16933
16934     case SOUND_CTRL_ID_LOOPS:
16935       if (setup.sound_loops)
16936         setup.sound_loops = FALSE;
16937       else if (audio.loops_available)
16938       {
16939         setup.sound = setup.sound_loops = TRUE;
16940
16941         SetAudioMode(setup.sound);
16942       }
16943       break;
16944
16945     case SOUND_CTRL_ID_SIMPLE:
16946       if (setup.sound_simple)
16947         setup.sound_simple = FALSE;
16948       else if (audio.sound_available)
16949       {
16950         setup.sound = setup.sound_simple = TRUE;
16951
16952         SetAudioMode(setup.sound);
16953       }
16954       break;
16955
16956     default:
16957       break;
16958   }
16959 }
16960
16961 static void HandleGameButtons(struct GadgetInfo *gi)
16962 {
16963   HandleGameButtonsExt(gi->custom_id);
16964 }
16965
16966 void HandleSoundButtonKeys(Key key)
16967 {
16968 #if 1
16969   if (key == setup.shortcut.sound_simple)
16970     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16971   else if (key == setup.shortcut.sound_loops)
16972     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16973   else if (key == setup.shortcut.sound_music)
16974     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16975 #else
16976   if (key == setup.shortcut.sound_simple)
16977     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16978   else if (key == setup.shortcut.sound_loops)
16979     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16980   else if (key == setup.shortcut.sound_music)
16981     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16982 #endif
16983 }