rnd-20100609-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_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   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2543              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2544
2545   /* redraw game control buttons */
2546 #if 1
2547   RedrawGameButtons();
2548 #else
2549   UnmapGameButtons();
2550   MapGameButtons();
2551 #endif
2552
2553   game_status = GAME_MODE_PSEUDO_PANEL;
2554
2555 #if 1
2556   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2557 #else
2558   for (i = 0; game_panel_controls[i].nr != -1; i++)
2559 #endif
2560   {
2561 #if 1
2562     int nr = game_panel_order[i].nr;
2563     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2564 #else
2565     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2566     int nr = gpc->nr;
2567 #endif
2568     struct TextPosInfo *pos = gpc->pos;
2569     int type = gpc->type;
2570     int value = gpc->value;
2571     int frame = gpc->frame;
2572 #if 0
2573     int last_value = gpc->last_value;
2574     int last_frame = gpc->last_frame;
2575 #endif
2576     int size = pos->size;
2577     int font = pos->font;
2578     boolean draw_masked = pos->draw_masked;
2579     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2580
2581     if (PANEL_DEACTIVATED(pos))
2582       continue;
2583
2584 #if 0
2585     if (value == last_value && frame == last_frame)
2586       continue;
2587 #endif
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592 #if 0
2593     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2594 #endif
2595
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599           nr == GAME_PANEL_TIME)
2600       {
2601         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603         if (use_dynamic_size)           /* use dynamic number of digits */
2604         {
2605           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607           int size2 = size1 + 1;
2608           int font1 = pos->font;
2609           int font2 = pos->font_alt;
2610
2611           size = (value < value_change ? size1 : size2);
2612           font = (value < value_change ? font1 : font2);
2613
2614 #if 0
2615           /* clear background if value just changed its size (dynamic digits) */
2616           if ((last_value < value_change) != (value < value_change))
2617           {
2618             int width1 = size1 * getFontWidth(font1);
2619             int width2 = size2 * getFontWidth(font2);
2620             int max_width = MAX(width1, width2);
2621             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2622
2623             pos->width = max_width;
2624
2625             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2626                                        max_width, max_height);
2627           }
2628 #endif
2629         }
2630       }
2631
2632 #if 1
2633       /* correct text size if "digits" is zero or less */
2634       if (size <= 0)
2635         size = strlen(int2str(value, size));
2636
2637       /* dynamically correct text alignment */
2638       pos->width = size * getFontWidth(font);
2639 #endif
2640
2641       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642                   int2str(value, size), font, mask_mode);
2643     }
2644     else if (type == TYPE_ELEMENT)
2645     {
2646       int element, graphic;
2647       Bitmap *src_bitmap;
2648       int src_x, src_y;
2649       int width, height;
2650       int dst_x = PANEL_XPOS(pos);
2651       int dst_y = PANEL_YPOS(pos);
2652
2653 #if 1
2654       if (value != EL_UNDEFINED && value != EL_EMPTY)
2655       {
2656         element = value;
2657         graphic = el2panelimg(value);
2658
2659         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2660
2661 #if 1
2662         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2663           size = TILESIZE;
2664 #endif
2665
2666         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2667                               &src_x, &src_y);
2668
2669         width  = graphic_info[graphic].width  * size / TILESIZE;
2670         height = graphic_info[graphic].height * size / TILESIZE;
2671
2672         if (draw_masked)
2673         {
2674           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2675                         dst_x - src_x, dst_y - src_y);
2676           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2677                            dst_x, dst_y);
2678         }
2679         else
2680         {
2681           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2682                      dst_x, dst_y);
2683         }
2684       }
2685 #else
2686       if (value == EL_UNDEFINED || value == EL_EMPTY)
2687       {
2688         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2689         graphic = el2panelimg(element);
2690
2691         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2692         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2693         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2694       }
2695       else
2696       {
2697         element = value;
2698         graphic = el2panelimg(value);
2699
2700         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2701       }
2702
2703       width  = graphic_info[graphic].width  * size / TILESIZE;
2704       height = graphic_info[graphic].height * size / TILESIZE;
2705
2706       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2707 #endif
2708     }
2709     else if (type == TYPE_STRING)
2710     {
2711       boolean active = (value != 0);
2712       char *state_normal = "off";
2713       char *state_active = "on";
2714       char *state = (active ? state_active : state_normal);
2715       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2716                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2717                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2718                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2719
2720       if (nr == GAME_PANEL_GRAVITY_STATE)
2721       {
2722         int font1 = pos->font;          /* (used for normal state) */
2723         int font2 = pos->font_alt;      /* (used for active state) */
2724 #if 0
2725         int size1 = strlen(state_normal);
2726         int size2 = strlen(state_active);
2727         int width1 = size1 * getFontWidth(font1);
2728         int width2 = size2 * getFontWidth(font2);
2729         int max_width = MAX(width1, width2);
2730         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2731
2732         pos->width = max_width;
2733
2734         /* clear background for values that may have changed its size */
2735         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736                                    max_width, max_height);
2737 #endif
2738
2739         font = (active ? font2 : font1);
2740       }
2741
2742       if (s != NULL)
2743       {
2744         char *s_cut;
2745
2746 #if 1
2747         if (size <= 0)
2748         {
2749           /* don't truncate output if "chars" is zero or less */
2750           size = strlen(s);
2751
2752           /* dynamically correct text alignment */
2753           pos->width = size * getFontWidth(font);
2754         }
2755 #endif
2756
2757         s_cut = getStringCopyN(s, size);
2758
2759         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2760                     s_cut, font, mask_mode);
2761
2762         free(s_cut);
2763       }
2764     }
2765
2766     redraw_mask |= REDRAW_DOOR_1;
2767   }
2768
2769   game_status = GAME_MODE_PLAYING;
2770 }
2771
2772 void UpdateAndDisplayGameControlValues()
2773 {
2774   if (tape.warp_forward)
2775     return;
2776
2777   UpdateGameControlValues();
2778   DisplayGameControlValues();
2779 }
2780
2781 void DrawGameValue_Emeralds(int value)
2782 {
2783   struct TextPosInfo *pos = &game.panel.gems;
2784 #if 1
2785   int font_nr = pos->font;
2786 #else
2787   int font_nr = FONT_TEXT_2;
2788 #endif
2789   int font_width = getFontWidth(font_nr);
2790   int chars = pos->size;
2791
2792 #if 1
2793   return;       /* !!! USE NEW STUFF !!! */
2794 #endif
2795
2796   if (PANEL_DEACTIVATED(pos))
2797     return;
2798
2799   pos->width = chars * font_width;
2800
2801   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2802 }
2803
2804 void DrawGameValue_Dynamite(int value)
2805 {
2806   struct TextPosInfo *pos = &game.panel.inventory_count;
2807 #if 1
2808   int font_nr = pos->font;
2809 #else
2810   int font_nr = FONT_TEXT_2;
2811 #endif
2812   int font_width = getFontWidth(font_nr);
2813   int chars = pos->size;
2814
2815 #if 1
2816   return;       /* !!! USE NEW STUFF !!! */
2817 #endif
2818
2819   if (PANEL_DEACTIVATED(pos))
2820     return;
2821
2822   pos->width = chars * font_width;
2823
2824   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2825 }
2826
2827 void DrawGameValue_Score(int value)
2828 {
2829   struct TextPosInfo *pos = &game.panel.score;
2830 #if 1
2831   int font_nr = pos->font;
2832 #else
2833   int font_nr = FONT_TEXT_2;
2834 #endif
2835   int font_width = getFontWidth(font_nr);
2836   int chars = pos->size;
2837
2838 #if 1
2839   return;       /* !!! USE NEW STUFF !!! */
2840 #endif
2841
2842   if (PANEL_DEACTIVATED(pos))
2843     return;
2844
2845   pos->width = chars * font_width;
2846
2847   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2848 }
2849
2850 void DrawGameValue_Time(int value)
2851 {
2852   struct TextPosInfo *pos = &game.panel.time;
2853   static int last_value = -1;
2854   int chars1 = 3;
2855   int chars2 = 4;
2856   int chars = pos->size;
2857 #if 1
2858   int font1_nr = pos->font;
2859   int font2_nr = pos->font_alt;
2860 #else
2861   int font1_nr = FONT_TEXT_2;
2862   int font2_nr = FONT_TEXT_1;
2863 #endif
2864   int font_nr = font1_nr;
2865   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866
2867 #if 1
2868   return;       /* !!! USE NEW STUFF !!! */
2869 #endif
2870
2871   if (PANEL_DEACTIVATED(pos))
2872     return;
2873
2874   if (use_dynamic_chars)                /* use dynamic number of chars */
2875   {
2876     chars   = (value < 1000 ? chars1   : chars2);
2877     font_nr = (value < 1000 ? font1_nr : font2_nr);
2878   }
2879
2880   /* clear background if value just changed its size (dynamic chars only) */
2881   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2882   {
2883     int width1 = chars1 * getFontWidth(font1_nr);
2884     int width2 = chars2 * getFontWidth(font2_nr);
2885     int max_width = MAX(width1, width2);
2886     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2887
2888     pos->width = max_width;
2889
2890     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2891                                max_width, max_height);
2892   }
2893
2894   pos->width = chars * getFontWidth(font_nr);
2895
2896   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2897
2898   last_value = value;
2899 }
2900
2901 void DrawGameValue_Level(int value)
2902 {
2903   struct TextPosInfo *pos = &game.panel.level_number;
2904   int chars1 = 2;
2905   int chars2 = 3;
2906   int chars = pos->size;
2907 #if 1
2908   int font1_nr = pos->font;
2909   int font2_nr = pos->font_alt;
2910 #else
2911   int font1_nr = FONT_TEXT_2;
2912   int font2_nr = FONT_TEXT_1;
2913 #endif
2914   int font_nr = font1_nr;
2915   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2916
2917 #if 1
2918   return;       /* !!! USE NEW STUFF !!! */
2919 #endif
2920
2921   if (PANEL_DEACTIVATED(pos))
2922     return;
2923
2924   if (use_dynamic_chars)                /* use dynamic number of chars */
2925   {
2926     chars   = (level_nr < 100 ? chars1   : chars2);
2927     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2928   }
2929
2930   pos->width = chars * getFontWidth(font_nr);
2931
2932   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2933 }
2934
2935 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2936 {
2937 #if 0
2938   struct TextPosInfo *pos = &game.panel.keys;
2939 #endif
2940 #if 0
2941   int base_key_graphic = EL_KEY_1;
2942 #endif
2943   int i;
2944
2945 #if 1
2946   return;       /* !!! USE NEW STUFF !!! */
2947 #endif
2948
2949 #if 0
2950   if (PANEL_DEACTIVATED(pos))
2951     return;
2952 #endif
2953
2954 #if 0
2955   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2956     base_key_graphic = EL_EM_KEY_1;
2957 #endif
2958
2959 #if 0
2960   pos->width = 4 * MINI_TILEX;
2961 #endif
2962
2963 #if 1
2964   for (i = 0; i < MAX_NUM_KEYS; i++)
2965 #else
2966   /* currently only 4 of 8 possible keys are displayed */
2967   for (i = 0; i < STD_NUM_KEYS; i++)
2968 #endif
2969   {
2970 #if 1
2971     struct TextPosInfo *pos = &game.panel.key[i];
2972 #endif
2973     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2974     int src_y = DOOR_GFX_PAGEY1 + 123;
2975 #if 1
2976     int dst_x = PANEL_XPOS(pos);
2977     int dst_y = PANEL_YPOS(pos);
2978 #else
2979     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2980     int dst_y = PANEL_YPOS(pos);
2981 #endif
2982
2983 #if 1
2984     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2985                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2986                    EL_KEY_1) + i;
2987     int graphic = el2edimg(element);
2988 #endif
2989
2990 #if 1
2991     if (PANEL_DEACTIVATED(pos))
2992       continue;
2993 #endif
2994
2995 #if 0
2996     /* masked blit with tiles from half-size scaled bitmap does not work yet
2997        (no mask bitmap created for these sizes after loading and scaling) --
2998        solution: load without creating mask, scale, then create final mask */
2999
3000     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3001                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3002
3003     if (key[i])
3004     {
3005 #if 0
3006       int graphic = el2edimg(base_key_graphic + i);
3007 #endif
3008       Bitmap *src_bitmap;
3009       int src_x, src_y;
3010
3011       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3012
3013       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3014                     dst_x - src_x, dst_y - src_y);
3015       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3016                        dst_x, dst_y);
3017     }
3018 #else
3019 #if 1
3020     if (key[i])
3021       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3022     else
3023       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3024                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3025 #else
3026     if (key[i])
3027       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3028     else
3029       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3030                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3031 #endif
3032 #endif
3033   }
3034 }
3035
3036 #else
3037
3038 void DrawGameValue_Emeralds(int value)
3039 {
3040   int font_nr = FONT_TEXT_2;
3041   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3042
3043   if (PANEL_DEACTIVATED(game.panel.gems))
3044     return;
3045
3046   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3047 }
3048
3049 void DrawGameValue_Dynamite(int value)
3050 {
3051   int font_nr = FONT_TEXT_2;
3052   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3053
3054   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3055     return;
3056
3057   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3058 }
3059
3060 void DrawGameValue_Score(int value)
3061 {
3062   int font_nr = FONT_TEXT_2;
3063   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3064
3065   if (PANEL_DEACTIVATED(game.panel.score))
3066     return;
3067
3068   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3069 }
3070
3071 void DrawGameValue_Time(int value)
3072 {
3073   int font1_nr = FONT_TEXT_2;
3074 #if 1
3075   int font2_nr = FONT_TEXT_1;
3076 #else
3077   int font2_nr = FONT_LEVEL_NUMBER;
3078 #endif
3079   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3080   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3081
3082   if (PANEL_DEACTIVATED(game.panel.time))
3083     return;
3084
3085   /* clear background if value just changed its size */
3086   if (value == 999 || value == 1000)
3087     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3088
3089   if (value < 1000)
3090     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3091   else
3092     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3093 }
3094
3095 void DrawGameValue_Level(int value)
3096 {
3097   int font1_nr = FONT_TEXT_2;
3098 #if 1
3099   int font2_nr = FONT_TEXT_1;
3100 #else
3101   int font2_nr = FONT_LEVEL_NUMBER;
3102 #endif
3103
3104   if (PANEL_DEACTIVATED(game.panel.level))
3105     return;
3106
3107   if (level_nr < 100)
3108     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3109   else
3110     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3111 }
3112
3113 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3114 {
3115   int base_key_graphic = EL_KEY_1;
3116   int i;
3117
3118   if (PANEL_DEACTIVATED(game.panel.keys))
3119     return;
3120
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3122     base_key_graphic = EL_EM_KEY_1;
3123
3124   /* currently only 4 of 8 possible keys are displayed */
3125   for (i = 0; i < STD_NUM_KEYS; i++)
3126   {
3127     int x = XX_KEYS + i * MINI_TILEX;
3128     int y = YY_KEYS;
3129
3130     if (key[i])
3131       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3132     else
3133       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3134                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3135   }
3136 }
3137
3138 #endif
3139
3140 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3141                        int key_bits)
3142 {
3143   int key[MAX_NUM_KEYS];
3144   int i;
3145
3146   /* prevent EM engine from updating time/score values parallel to GameWon() */
3147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3148       local_player->LevelSolved)
3149     return;
3150
3151   for (i = 0; i < MAX_NUM_KEYS; i++)
3152     key[i] = key_bits & (1 << i);
3153
3154   DrawGameValue_Level(level_nr);
3155
3156   DrawGameValue_Emeralds(emeralds);
3157   DrawGameValue_Dynamite(dynamite);
3158   DrawGameValue_Score(score);
3159   DrawGameValue_Time(time);
3160
3161   DrawGameValue_Keys(key);
3162 }
3163
3164 void UpdateGameDoorValues()
3165 {
3166   UpdateGameControlValues();
3167 }
3168
3169 void DrawGameDoorValues()
3170 {
3171   DisplayGameControlValues();
3172 }
3173
3174 void DrawGameDoorValues_OLD()
3175 {
3176   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3177   int dynamite_value = 0;
3178   int score_value = (local_player->LevelSolved ? local_player->score_final :
3179                      local_player->score);
3180   int gems_value = local_player->gems_still_needed;
3181   int key_bits = 0;
3182   int i, j;
3183
3184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3185   {
3186     DrawGameDoorValues_EM();
3187
3188     return;
3189   }
3190
3191   if (game.centered_player_nr == -1)
3192   {
3193     for (i = 0; i < MAX_PLAYERS; i++)
3194     {
3195       for (j = 0; j < MAX_NUM_KEYS; j++)
3196         if (stored_player[i].key[j])
3197           key_bits |= (1 << j);
3198
3199       dynamite_value += stored_player[i].inventory_size;
3200     }
3201   }
3202   else
3203   {
3204     int player_nr = game.centered_player_nr;
3205
3206     for (i = 0; i < MAX_NUM_KEYS; i++)
3207       if (stored_player[player_nr].key[i])
3208         key_bits |= (1 << i);
3209
3210     dynamite_value = stored_player[player_nr].inventory_size;
3211   }
3212
3213   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3214                     key_bits);
3215 }
3216
3217
3218 /*
3219   =============================================================================
3220   InitGameEngine()
3221   -----------------------------------------------------------------------------
3222   initialize game engine due to level / tape version number
3223   =============================================================================
3224 */
3225
3226 static void InitGameEngine()
3227 {
3228   int i, j, k, l, x, y;
3229
3230   /* set game engine from tape file when re-playing, else from level file */
3231   game.engine_version = (tape.playing ? tape.engine_version :
3232                          level.game_version);
3233
3234   /* ---------------------------------------------------------------------- */
3235   /* set flags for bugs and changes according to active game engine version */
3236   /* ---------------------------------------------------------------------- */
3237
3238   /*
3239     Summary of bugfix/change:
3240     Fixed handling for custom elements that change when pushed by the player.
3241
3242     Fixed/changed in version:
3243     3.1.0
3244
3245     Description:
3246     Before 3.1.0, custom elements that "change when pushing" changed directly
3247     after the player started pushing them (until then handled in "DigField()").
3248     Since 3.1.0, these custom elements are not changed until the "pushing"
3249     move of the element is finished (now handled in "ContinueMoving()").
3250
3251     Affected levels/tapes:
3252     The first condition is generally needed for all levels/tapes before version
3253     3.1.0, which might use the old behaviour before it was changed; known tapes
3254     that are affected are some tapes from the level set "Walpurgis Gardens" by
3255     Jamie Cullen.
3256     The second condition is an exception from the above case and is needed for
3257     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3258     above (including some development versions of 3.1.0), but before it was
3259     known that this change would break tapes like the above and was fixed in
3260     3.1.1, so that the changed behaviour was active although the engine version
3261     while recording maybe was before 3.1.0. There is at least one tape that is
3262     affected by this exception, which is the tape for the one-level set "Bug
3263     Machine" by Juergen Bonhagen.
3264   */
3265
3266   game.use_change_when_pushing_bug =
3267     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3268      !(tape.playing &&
3269        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3270        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3271
3272   /*
3273     Summary of bugfix/change:
3274     Fixed handling for blocking the field the player leaves when moving.
3275
3276     Fixed/changed in version:
3277     3.1.1
3278
3279     Description:
3280     Before 3.1.1, when "block last field when moving" was enabled, the field
3281     the player is leaving when moving was blocked for the time of the move,
3282     and was directly unblocked afterwards. This resulted in the last field
3283     being blocked for exactly one less than the number of frames of one player
3284     move. Additionally, even when blocking was disabled, the last field was
3285     blocked for exactly one frame.
3286     Since 3.1.1, due to changes in player movement handling, the last field
3287     is not blocked at all when blocking is disabled. When blocking is enabled,
3288     the last field is blocked for exactly the number of frames of one player
3289     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3290     last field is blocked for exactly one more than the number of frames of
3291     one player move.
3292
3293     Affected levels/tapes:
3294     (!!! yet to be determined -- probably many !!!)
3295   */
3296
3297   game.use_block_last_field_bug =
3298     (game.engine_version < VERSION_IDENT(3,1,1,0));
3299
3300   /*
3301     Summary of bugfix/change:
3302     Changed behaviour of CE changes with multiple changes per single frame.
3303
3304     Fixed/changed in version:
3305     3.2.0-6
3306
3307     Description:
3308     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3309     This resulted in race conditions where CEs seem to behave strange in some
3310     situations (where triggered CE changes were just skipped because there was
3311     already a CE change on that tile in the playfield in that engine frame).
3312     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3313     (The number of changes per frame must be limited in any case, because else
3314     it is easily possible to define CE changes that would result in an infinite
3315     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3316     should be set large enough so that it would only be reached in cases where
3317     the corresponding CE change conditions run into a loop. Therefore, it seems
3318     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3319     maximal number of change pages for custom elements.)
3320
3321     Affected levels/tapes:
3322     Probably many.
3323   */
3324
3325 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3326   game.max_num_changes_per_frame = 1;
3327 #else
3328   game.max_num_changes_per_frame =
3329     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3330 #endif
3331
3332   /* ---------------------------------------------------------------------- */
3333
3334   /* default scan direction: scan playfield from top/left to bottom/right */
3335   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3336
3337   /* dynamically adjust element properties according to game engine version */
3338   InitElementPropertiesEngine(game.engine_version);
3339
3340 #if 0
3341   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3342   printf("          tape version == %06d [%s] [file: %06d]\n",
3343          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3344          tape.file_version);
3345   printf("       => game.engine_version == %06d\n", game.engine_version);
3346 #endif
3347
3348   /* ---------- initialize player's initial move delay --------------------- */
3349
3350   /* dynamically adjust player properties according to level information */
3351   for (i = 0; i < MAX_PLAYERS; i++)
3352     game.initial_move_delay_value[i] =
3353       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3354
3355   /* dynamically adjust player properties according to game engine version */
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357     game.initial_move_delay[i] =
3358       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3359        game.initial_move_delay_value[i] : 0);
3360
3361   /* ---------- initialize player's initial push delay --------------------- */
3362
3363   /* dynamically adjust player properties according to game engine version */
3364   game.initial_push_delay_value =
3365     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3366
3367   /* ---------- initialize changing elements ------------------------------- */
3368
3369   /* initialize changing elements information */
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371   {
3372     struct ElementInfo *ei = &element_info[i];
3373
3374     /* this pointer might have been changed in the level editor */
3375     ei->change = &ei->change_page[0];
3376
3377     if (!IS_CUSTOM_ELEMENT(i))
3378     {
3379       ei->change->target_element = EL_EMPTY_SPACE;
3380       ei->change->delay_fixed = 0;
3381       ei->change->delay_random = 0;
3382       ei->change->delay_frames = 1;
3383     }
3384
3385     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3386     {
3387       ei->has_change_event[j] = FALSE;
3388
3389       ei->event_page_nr[j] = 0;
3390       ei->event_page[j] = &ei->change_page[0];
3391     }
3392   }
3393
3394   /* add changing elements from pre-defined list */
3395   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3396   {
3397     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3398     struct ElementInfo *ei = &element_info[ch_delay->element];
3399
3400     ei->change->target_element       = ch_delay->target_element;
3401     ei->change->delay_fixed          = ch_delay->change_delay;
3402
3403     ei->change->pre_change_function  = ch_delay->pre_change_function;
3404     ei->change->change_function      = ch_delay->change_function;
3405     ei->change->post_change_function = ch_delay->post_change_function;
3406
3407     ei->change->can_change = TRUE;
3408     ei->change->can_change_or_has_action = TRUE;
3409
3410     ei->has_change_event[CE_DELAY] = TRUE;
3411
3412     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3413     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3414   }
3415
3416   /* ---------- initialize internal run-time variables --------------------- */
3417
3418   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3419   {
3420     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3421
3422     for (j = 0; j < ei->num_change_pages; j++)
3423     {
3424       ei->change_page[j].can_change_or_has_action =
3425         (ei->change_page[j].can_change |
3426          ei->change_page[j].has_action);
3427     }
3428   }
3429
3430   /* add change events from custom element configuration */
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3434
3435     for (j = 0; j < ei->num_change_pages; j++)
3436     {
3437       if (!ei->change_page[j].can_change_or_has_action)
3438         continue;
3439
3440       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3441       {
3442         /* only add event page for the first page found with this event */
3443         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3444         {
3445           ei->has_change_event[k] = TRUE;
3446
3447           ei->event_page_nr[k] = j;
3448           ei->event_page[k] = &ei->change_page[j];
3449         }
3450       }
3451     }
3452   }
3453
3454 #if 1
3455   /* ---------- initialize reference elements in change conditions --------- */
3456
3457   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3458   {
3459     int element = EL_CUSTOM_START + i;
3460     struct ElementInfo *ei = &element_info[element];
3461
3462     for (j = 0; j < ei->num_change_pages; j++)
3463     {
3464       int trigger_element = ei->change_page[j].initial_trigger_element;
3465
3466       if (trigger_element >= EL_PREV_CE_8 &&
3467           trigger_element <= EL_NEXT_CE_8)
3468         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3469
3470       ei->change_page[j].trigger_element = trigger_element;
3471     }
3472   }
3473 #endif
3474
3475   /* ---------- initialize run-time trigger player and element ------------- */
3476
3477   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3478   {
3479     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3480
3481     for (j = 0; j < ei->num_change_pages; j++)
3482     {
3483       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3484       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3485       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3486       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3487       ei->change_page[j].actual_trigger_ce_value = 0;
3488       ei->change_page[j].actual_trigger_ce_score = 0;
3489     }
3490   }
3491
3492   /* ---------- initialize trigger events ---------------------------------- */
3493
3494   /* initialize trigger events information */
3495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3497       trigger_events[i][j] = FALSE;
3498
3499   /* add trigger events from element change event properties */
3500   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3501   {
3502     struct ElementInfo *ei = &element_info[i];
3503
3504     for (j = 0; j < ei->num_change_pages; j++)
3505     {
3506       if (!ei->change_page[j].can_change_or_has_action)
3507         continue;
3508
3509       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3510       {
3511         int trigger_element = ei->change_page[j].trigger_element;
3512
3513         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3514         {
3515           if (ei->change_page[j].has_event[k])
3516           {
3517             if (IS_GROUP_ELEMENT(trigger_element))
3518             {
3519               struct ElementGroupInfo *group =
3520                 element_info[trigger_element].group;
3521
3522               for (l = 0; l < group->num_elements_resolved; l++)
3523                 trigger_events[group->element_resolved[l]][k] = TRUE;
3524             }
3525             else if (trigger_element == EL_ANY_ELEMENT)
3526               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3527                 trigger_events[l][k] = TRUE;
3528             else
3529               trigger_events[trigger_element][k] = TRUE;
3530           }
3531         }
3532       }
3533     }
3534   }
3535
3536   /* ---------- initialize push delay -------------------------------------- */
3537
3538   /* initialize push delay values to default */
3539   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540   {
3541     if (!IS_CUSTOM_ELEMENT(i))
3542     {
3543       /* set default push delay values (corrected since version 3.0.7-1) */
3544       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3545       {
3546         element_info[i].push_delay_fixed = 2;
3547         element_info[i].push_delay_random = 8;
3548       }
3549       else
3550       {
3551         element_info[i].push_delay_fixed = 8;
3552         element_info[i].push_delay_random = 8;
3553       }
3554     }
3555   }
3556
3557   /* set push delay value for certain elements from pre-defined list */
3558   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3559   {
3560     int e = push_delay_list[i].element;
3561
3562     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3563     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3564   }
3565
3566   /* set push delay value for Supaplex elements for newer engine versions */
3567   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3568   {
3569     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570     {
3571       if (IS_SP_ELEMENT(i))
3572       {
3573         /* set SP push delay to just enough to push under a falling zonk */
3574         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3575
3576         element_info[i].push_delay_fixed  = delay;
3577         element_info[i].push_delay_random = 0;
3578       }
3579     }
3580   }
3581
3582   /* ---------- initialize move stepsize ----------------------------------- */
3583
3584   /* initialize move stepsize values to default */
3585   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586     if (!IS_CUSTOM_ELEMENT(i))
3587       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3588
3589   /* set move stepsize value for certain elements from pre-defined list */
3590   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3591   {
3592     int e = move_stepsize_list[i].element;
3593
3594     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3595   }
3596
3597   /* ---------- initialize collect score ----------------------------------- */
3598
3599   /* initialize collect score values for custom elements from initial value */
3600   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601     if (IS_CUSTOM_ELEMENT(i))
3602       element_info[i].collect_score = element_info[i].collect_score_initial;
3603
3604   /* ---------- initialize collect count ----------------------------------- */
3605
3606   /* initialize collect count values for non-custom elements */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (!IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_count_initial = 0;
3610
3611   /* add collect count values for all elements from pre-defined list */
3612   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3613     element_info[collect_count_list[i].element].collect_count_initial =
3614       collect_count_list[i].count;
3615
3616   /* ---------- initialize access direction -------------------------------- */
3617
3618   /* initialize access direction values to default (access from every side) */
3619   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3620     if (!IS_CUSTOM_ELEMENT(i))
3621       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3622
3623   /* set access direction value for certain elements from pre-defined list */
3624   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3625     element_info[access_direction_list[i].element].access_direction =
3626       access_direction_list[i].direction;
3627
3628   /* ---------- initialize explosion content ------------------------------- */
3629   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3630   {
3631     if (IS_CUSTOM_ELEMENT(i))
3632       continue;
3633
3634     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3635     {
3636       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3637
3638       element_info[i].content.e[x][y] =
3639         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3640          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3641          i == EL_PLAYER_3 ? EL_EMERALD :
3642          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3643          i == EL_MOLE ? EL_EMERALD_RED :
3644          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3645          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3646          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3647          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3648          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3649          i == EL_WALL_EMERALD ? EL_EMERALD :
3650          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3651          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3652          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3653          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3654          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3655          i == EL_WALL_PEARL ? EL_PEARL :
3656          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3657          EL_EMPTY);
3658     }
3659   }
3660
3661   /* ---------- initialize recursion detection ------------------------------ */
3662   recursion_loop_depth = 0;
3663   recursion_loop_detected = FALSE;
3664   recursion_loop_element = EL_UNDEFINED;
3665
3666   /* ---------- initialize graphics engine ---------------------------------- */
3667   game.scroll_delay_value =
3668     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3669      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3670   game.scroll_delay_value =
3671     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3672 }
3673
3674 int get_num_special_action(int element, int action_first, int action_last)
3675 {
3676   int num_special_action = 0;
3677   int i, j;
3678
3679   for (i = action_first; i <= action_last; i++)
3680   {
3681     boolean found = FALSE;
3682
3683     for (j = 0; j < NUM_DIRECTIONS; j++)
3684       if (el_act_dir2img(element, i, j) !=
3685           el_act_dir2img(element, ACTION_DEFAULT, j))
3686         found = TRUE;
3687
3688     if (found)
3689       num_special_action++;
3690     else
3691       break;
3692   }
3693
3694   return num_special_action;
3695 }
3696
3697
3698 /*
3699   =============================================================================
3700   InitGame()
3701   -----------------------------------------------------------------------------
3702   initialize and start new game
3703   =============================================================================
3704 */
3705
3706 void InitGame()
3707 {
3708   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3709   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3710   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3711 #if 0
3712   boolean do_fading = (game_status == GAME_MODE_MAIN);
3713 #endif
3714 #if 1
3715   int initial_move_dir = MV_DOWN;
3716 #else
3717   int initial_move_dir = MV_NONE;
3718 #endif
3719   int i, j, x, y;
3720
3721   game_status = GAME_MODE_PLAYING;
3722
3723 #if 1
3724   /* needed if different viewport properties defined for playing */
3725   ChangeViewportPropertiesIfNeeded();
3726 #endif
3727
3728 #if 1
3729   DrawCompleteVideoDisplay();
3730 #endif
3731
3732   InitGameEngine();
3733   InitGameControlValues();
3734
3735   /* don't play tapes over network */
3736   network_playing = (options.network && !tape.playing);
3737
3738   for (i = 0; i < MAX_PLAYERS; i++)
3739   {
3740     struct PlayerInfo *player = &stored_player[i];
3741
3742     player->index_nr = i;
3743     player->index_bit = (1 << i);
3744     player->element_nr = EL_PLAYER_1 + i;
3745
3746     player->present = FALSE;
3747     player->active = FALSE;
3748     player->mapped = FALSE;
3749
3750     player->killed = FALSE;
3751     player->reanimated = FALSE;
3752
3753     player->action = 0;
3754     player->effective_action = 0;
3755     player->programmed_action = 0;
3756
3757     player->score = 0;
3758     player->score_final = 0;
3759
3760     player->gems_still_needed = level.gems_needed;
3761     player->sokobanfields_still_needed = 0;
3762     player->lights_still_needed = 0;
3763     player->friends_still_needed = 0;
3764
3765     for (j = 0; j < MAX_NUM_KEYS; j++)
3766       player->key[j] = FALSE;
3767
3768     player->num_white_keys = 0;
3769
3770     player->dynabomb_count = 0;
3771     player->dynabomb_size = 1;
3772     player->dynabombs_left = 0;
3773     player->dynabomb_xl = FALSE;
3774
3775     player->MovDir = initial_move_dir;
3776     player->MovPos = 0;
3777     player->GfxPos = 0;
3778     player->GfxDir = initial_move_dir;
3779     player->GfxAction = ACTION_DEFAULT;
3780     player->Frame = 0;
3781     player->StepFrame = 0;
3782
3783     player->initial_element = player->element_nr;
3784     player->artwork_element =
3785       (level.use_artwork_element[i] ? level.artwork_element[i] :
3786        player->element_nr);
3787     player->use_murphy = FALSE;
3788
3789     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3790     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3791
3792     player->gravity = level.initial_player_gravity[i];
3793
3794     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3795
3796     player->actual_frame_counter = 0;
3797
3798     player->step_counter = 0;
3799
3800     player->last_move_dir = initial_move_dir;
3801
3802     player->is_active = FALSE;
3803
3804     player->is_waiting = FALSE;
3805     player->is_moving = FALSE;
3806     player->is_auto_moving = FALSE;
3807     player->is_digging = FALSE;
3808     player->is_snapping = FALSE;
3809     player->is_collecting = FALSE;
3810     player->is_pushing = FALSE;
3811     player->is_switching = FALSE;
3812     player->is_dropping = FALSE;
3813     player->is_dropping_pressed = FALSE;
3814
3815     player->is_bored = FALSE;
3816     player->is_sleeping = FALSE;
3817
3818     player->frame_counter_bored = -1;
3819     player->frame_counter_sleeping = -1;
3820
3821     player->anim_delay_counter = 0;
3822     player->post_delay_counter = 0;
3823
3824     player->dir_waiting = initial_move_dir;
3825     player->action_waiting = ACTION_DEFAULT;
3826     player->last_action_waiting = ACTION_DEFAULT;
3827     player->special_action_bored = ACTION_DEFAULT;
3828     player->special_action_sleeping = ACTION_DEFAULT;
3829
3830     player->switch_x = -1;
3831     player->switch_y = -1;
3832
3833     player->drop_x = -1;
3834     player->drop_y = -1;
3835
3836     player->show_envelope = 0;
3837
3838     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3839
3840     player->push_delay       = -1;      /* initialized when pushing starts */
3841     player->push_delay_value = game.initial_push_delay_value;
3842
3843     player->drop_delay = 0;
3844     player->drop_pressed_delay = 0;
3845
3846     player->last_jx = -1;
3847     player->last_jy = -1;
3848     player->jx = -1;
3849     player->jy = -1;
3850
3851     player->shield_normal_time_left = 0;
3852     player->shield_deadly_time_left = 0;
3853
3854     player->inventory_infinite_element = EL_UNDEFINED;
3855     player->inventory_size = 0;
3856
3857     if (level.use_initial_inventory[i])
3858     {
3859       for (j = 0; j < level.initial_inventory_size[i]; j++)
3860       {
3861         int element = level.initial_inventory_content[i][j];
3862         int collect_count = element_info[element].collect_count_initial;
3863         int k;
3864
3865         if (!IS_CUSTOM_ELEMENT(element))
3866           collect_count = 1;
3867
3868         if (collect_count == 0)
3869           player->inventory_infinite_element = element;
3870         else
3871           for (k = 0; k < collect_count; k++)
3872             if (player->inventory_size < MAX_INVENTORY_SIZE)
3873               player->inventory_element[player->inventory_size++] = element;
3874       }
3875     }
3876
3877     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3878     SnapField(player, 0, 0);
3879
3880     player->LevelSolved = FALSE;
3881     player->GameOver = FALSE;
3882
3883     player->LevelSolved_GameWon = FALSE;
3884     player->LevelSolved_GameEnd = FALSE;
3885     player->LevelSolved_PanelOff = FALSE;
3886     player->LevelSolved_SaveTape = FALSE;
3887     player->LevelSolved_SaveScore = FALSE;
3888     player->LevelSolved_CountingTime = 0;
3889     player->LevelSolved_CountingScore = 0;
3890
3891     map_player_action[i] = i;
3892   }
3893
3894   network_player_action_received = FALSE;
3895
3896 #if defined(NETWORK_AVALIABLE)
3897   /* initial null action */
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900 #endif
3901
3902   ZX = ZY = -1;
3903   ExitX = ExitY = -1;
3904
3905   FrameCounter = 0;
3906   TimeFrames = 0;
3907   TimePlayed = 0;
3908   TimeLeft = level.time;
3909   TapeTime = 0;
3910
3911   ScreenMovDir = MV_NONE;
3912   ScreenMovPos = 0;
3913   ScreenGfxPos = 0;
3914
3915   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3916
3917   AllPlayersGone = FALSE;
3918
3919   game.yamyam_content_nr = 0;
3920   game.robot_wheel_active = FALSE;
3921   game.magic_wall_active = FALSE;
3922   game.magic_wall_time_left = 0;
3923   game.light_time_left = 0;
3924   game.timegate_time_left = 0;
3925   game.switchgate_pos = 0;
3926   game.wind_direction = level.wind_direction_initial;
3927
3928 #if !USE_PLAYER_GRAVITY
3929   game.gravity = FALSE;
3930   game.explosions_delayed = TRUE;
3931 #endif
3932
3933   game.lenses_time_left = 0;
3934   game.magnify_time_left = 0;
3935
3936   game.ball_state = level.ball_state_initial;
3937   game.ball_content_nr = 0;
3938
3939   game.envelope_active = FALSE;
3940
3941   /* set focus to local player for network games, else to all players */
3942   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3943   game.centered_player_nr_next = game.centered_player_nr;
3944   game.set_centered_player = FALSE;
3945
3946   if (network_playing && tape.recording)
3947   {
3948     /* store client dependent player focus when recording network games */
3949     tape.centered_player_nr_next = game.centered_player_nr_next;
3950     tape.set_centered_player = TRUE;
3951   }
3952
3953   for (i = 0; i < NUM_BELTS; i++)
3954   {
3955     game.belt_dir[i] = MV_NONE;
3956     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3957   }
3958
3959   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3960     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3961
3962   SCAN_PLAYFIELD(x, y)
3963   {
3964     Feld[x][y] = level.field[x][y];
3965     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3966     ChangeDelay[x][y] = 0;
3967     ChangePage[x][y] = -1;
3968 #if USE_NEW_CUSTOM_VALUE
3969     CustomValue[x][y] = 0;              /* initialized in InitField() */
3970 #endif
3971     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3972     AmoebaNr[x][y] = 0;
3973     WasJustMoving[x][y] = 0;
3974     WasJustFalling[x][y] = 0;
3975     CheckCollision[x][y] = 0;
3976     CheckImpact[x][y] = 0;
3977     Stop[x][y] = FALSE;
3978     Pushed[x][y] = FALSE;
3979
3980     ChangeCount[x][y] = 0;
3981     ChangeEvent[x][y] = -1;
3982
3983     ExplodePhase[x][y] = 0;
3984     ExplodeDelay[x][y] = 0;
3985     ExplodeField[x][y] = EX_TYPE_NONE;
3986
3987     RunnerVisit[x][y] = 0;
3988     PlayerVisit[x][y] = 0;
3989
3990     GfxFrame[x][y] = 0;
3991     GfxRandom[x][y] = INIT_GFX_RANDOM();
3992     GfxElement[x][y] = EL_UNDEFINED;
3993     GfxAction[x][y] = ACTION_DEFAULT;
3994     GfxDir[x][y] = MV_NONE;
3995     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3996   }
3997
3998   SCAN_PLAYFIELD(x, y)
3999   {
4000     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4001       emulate_bd = FALSE;
4002     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4003       emulate_sb = FALSE;
4004     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4005       emulate_sp = FALSE;
4006
4007     InitField(x, y, TRUE);
4008
4009     ResetGfxAnimation(x, y);
4010   }
4011
4012   InitBeltMovement();
4013
4014   for (i = 0; i < MAX_PLAYERS; i++)
4015   {
4016     struct PlayerInfo *player = &stored_player[i];
4017
4018     /* set number of special actions for bored and sleeping animation */
4019     player->num_special_action_bored =
4020       get_num_special_action(player->artwork_element,
4021                              ACTION_BORING_1, ACTION_BORING_LAST);
4022     player->num_special_action_sleeping =
4023       get_num_special_action(player->artwork_element,
4024                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4025   }
4026
4027   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028                     emulate_sb ? EMU_SOKOBAN :
4029                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4030
4031 #if USE_NEW_ALL_SLIPPERY
4032   /* initialize type of slippery elements */
4033   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4034   {
4035     if (!IS_CUSTOM_ELEMENT(i))
4036     {
4037       /* default: elements slip down either to the left or right randomly */
4038       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4039
4040       /* SP style elements prefer to slip down on the left side */
4041       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4042         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4043
4044       /* BD style elements prefer to slip down on the left side */
4045       if (game.emulation == EMU_BOULDERDASH)
4046         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4047     }
4048   }
4049 #endif
4050
4051   /* initialize explosion and ignition delay */
4052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4053   {
4054     if (!IS_CUSTOM_ELEMENT(i))
4055     {
4056       int num_phase = 8;
4057       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4058                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4059                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4060       int last_phase = (num_phase + 1) * delay;
4061       int half_phase = (num_phase / 2) * delay;
4062
4063       element_info[i].explosion_delay = last_phase - 1;
4064       element_info[i].ignition_delay = half_phase;
4065
4066       if (i == EL_BLACK_ORB)
4067         element_info[i].ignition_delay = 1;
4068     }
4069
4070 #if 0
4071     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4072       element_info[i].explosion_delay = 1;
4073
4074     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4075       element_info[i].ignition_delay = 1;
4076 #endif
4077   }
4078
4079   /* correct non-moving belts to start moving left */
4080   for (i = 0; i < NUM_BELTS; i++)
4081     if (game.belt_dir[i] == MV_NONE)
4082       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4083
4084 #if USE_NEW_PLAYER_ASSIGNMENTS
4085   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4086   /* choose default local player */
4087   local_player = &stored_player[0];
4088
4089   for (i = 0; i < MAX_PLAYERS; i++)
4090     stored_player[i].connected = FALSE;
4091
4092   local_player->connected = TRUE;
4093   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4094
4095   if (tape.playing)
4096   {
4097     /* try to guess locally connected team mode players (needed for correct
4098        assignment of player figures from level to locally playing players) */
4099
4100     for (i = 0; i < MAX_PLAYERS; i++)
4101       if (tape.player_participates[i])
4102         stored_player[i].connected = TRUE;
4103   }
4104   else if (setup.team_mode && !options.network)
4105   {
4106     /* try to guess locally connected team mode players (needed for correct
4107        assignment of player figures from level to locally playing players) */
4108
4109     for (i = 0; i < MAX_PLAYERS; i++)
4110       if (setup.input[i].use_joystick ||
4111           setup.input[i].key.left != KSYM_UNDEFINED)
4112         stored_player[i].connected = TRUE;
4113   }
4114
4115 #if 0
4116   for (i = 0; i < MAX_PLAYERS; i++)
4117     printf("::: player %d: %s\n", i,
4118            (stored_player[i].connected ? "connected" : "not connected"));
4119
4120   for (i = 0; i < MAX_PLAYERS; i++)
4121     printf("::: player %d: %s\n", i,
4122            (stored_player[i].present ? "present" : "not present"));
4123 #endif
4124
4125   /* check if any connected player was not found in playfield */
4126   for (i = 0; i < MAX_PLAYERS; i++)
4127   {
4128     struct PlayerInfo *player = &stored_player[i];
4129
4130     if (player->connected && !player->present)
4131     {
4132       struct PlayerInfo *field_player = NULL;
4133
4134 #if 0
4135       printf("::: looking for field player for player %d ...\n", i);
4136 #endif
4137
4138       /* assign first free player found that is present in the playfield */
4139
4140       /* first try: look for unmapped playfield player that is not connected */
4141       if (field_player == NULL)
4142         for (j = 0; j < MAX_PLAYERS; j++)
4143           if (stored_player[j].present &&
4144               !stored_player[j].mapped &&
4145               !stored_player[j].connected)
4146             field_player = &stored_player[j];
4147
4148       /* second try: look for *any* unmapped playfield player */
4149       if (field_player == NULL)
4150         for (j = 0; j < MAX_PLAYERS; j++)
4151           if (stored_player[j].present &&
4152               !stored_player[j].mapped)
4153             field_player = &stored_player[j];
4154
4155       if (field_player != NULL)
4156       {
4157         int jx = field_player->jx, jy = field_player->jy;
4158
4159 #if 0
4160         printf("::: found player figure %d\n", field_player->index_nr);
4161 #endif
4162
4163         player->present = FALSE;
4164         player->active = FALSE;
4165
4166         field_player->present = TRUE;
4167         field_player->active = TRUE;
4168
4169         /*
4170         player->initial_element = field_player->initial_element;
4171         player->artwork_element = field_player->artwork_element;
4172
4173         player->block_last_field       = field_player->block_last_field;
4174         player->block_delay_adjustment = field_player->block_delay_adjustment;
4175         */
4176
4177         StorePlayer[jx][jy] = field_player->element_nr;
4178
4179         field_player->jx = field_player->last_jx = jx;
4180         field_player->jy = field_player->last_jy = jy;
4181
4182         if (local_player == player)
4183           local_player = field_player;
4184
4185         map_player_action[field_player->index_nr] = i;
4186
4187         field_player->mapped = TRUE;
4188
4189 #if 0
4190         printf("::: map_player_action[%d] == %d\n",
4191                field_player->index_nr, i);
4192 #endif
4193       }
4194     }
4195
4196     if (player->connected && player->present)
4197       player->mapped = TRUE;
4198   }
4199
4200 #else
4201
4202   /* check if any connected player was not found in playfield */
4203   for (i = 0; i < MAX_PLAYERS; i++)
4204   {
4205     struct PlayerInfo *player = &stored_player[i];
4206
4207     if (player->connected && !player->present)
4208     {
4209       for (j = 0; j < MAX_PLAYERS; j++)
4210       {
4211         struct PlayerInfo *field_player = &stored_player[j];
4212         int jx = field_player->jx, jy = field_player->jy;
4213
4214         /* assign first free player found that is present in the playfield */
4215         if (field_player->present && !field_player->connected)
4216         {
4217           player->present = TRUE;
4218           player->active = TRUE;
4219
4220           field_player->present = FALSE;
4221           field_player->active = FALSE;
4222
4223           player->initial_element = field_player->initial_element;
4224           player->artwork_element = field_player->artwork_element;
4225
4226           player->block_last_field       = field_player->block_last_field;
4227           player->block_delay_adjustment = field_player->block_delay_adjustment;
4228
4229           StorePlayer[jx][jy] = player->element_nr;
4230
4231           player->jx = player->last_jx = jx;
4232           player->jy = player->last_jy = jy;
4233
4234           break;
4235         }
4236       }
4237     }
4238   }
4239 #endif
4240
4241 #if 0
4242   printf("::: local_player->present == %d\n", local_player->present);
4243 #endif
4244
4245   if (tape.playing)
4246   {
4247     /* when playing a tape, eliminate all players who do not participate */
4248
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251     {
4252       if (stored_player[i].active &&
4253           !tape.player_participates[map_player_action[i]])
4254       {
4255         struct PlayerInfo *player = &stored_player[i];
4256         int jx = player->jx, jy = player->jy;
4257
4258         player->active = FALSE;
4259         StorePlayer[jx][jy] = 0;
4260         Feld[jx][jy] = EL_EMPTY;
4261       }
4262     }
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265     {
4266       if (stored_player[i].active &&
4267           !tape.player_participates[i])
4268       {
4269         struct PlayerInfo *player = &stored_player[i];
4270         int jx = player->jx, jy = player->jy;
4271
4272         player->active = FALSE;
4273         StorePlayer[jx][jy] = 0;
4274         Feld[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277 #endif
4278   }
4279   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4280   {
4281     /* when in single player mode, eliminate all but the first active player */
4282
4283     for (i = 0; i < MAX_PLAYERS; i++)
4284     {
4285       if (stored_player[i].active)
4286       {
4287         for (j = i + 1; j < MAX_PLAYERS; j++)
4288         {
4289           if (stored_player[j].active)
4290           {
4291             struct PlayerInfo *player = &stored_player[j];
4292             int jx = player->jx, jy = player->jy;
4293
4294             player->active = FALSE;
4295             player->present = FALSE;
4296
4297             StorePlayer[jx][jy] = 0;
4298             Feld[jx][jy] = EL_EMPTY;
4299           }
4300         }
4301       }
4302     }
4303   }
4304
4305   /* when recording the game, store which players take part in the game */
4306   if (tape.recording)
4307   {
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].connected)
4311         tape.player_participates[i] = TRUE;
4312 #else
4313     for (i = 0; i < MAX_PLAYERS; i++)
4314       if (stored_player[i].active)
4315         tape.player_participates[i] = TRUE;
4316 #endif
4317   }
4318
4319   if (options.debug)
4320   {
4321     for (i = 0; i < MAX_PLAYERS; i++)
4322     {
4323       struct PlayerInfo *player = &stored_player[i];
4324
4325       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4326              i+1,
4327              player->present,
4328              player->connected,
4329              player->active);
4330       if (local_player == player)
4331         printf("Player  %d is local player.\n", i+1);
4332     }
4333   }
4334
4335   if (BorderElement == EL_EMPTY)
4336   {
4337     SBX_Left = 0;
4338     SBX_Right = lev_fieldx - SCR_FIELDX;
4339     SBY_Upper = 0;
4340     SBY_Lower = lev_fieldy - SCR_FIELDY;
4341   }
4342   else
4343   {
4344     SBX_Left = -1;
4345     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4346     SBY_Upper = -1;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4348   }
4349
4350   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4351     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4352
4353   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4354     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4355
4356   /* if local player not found, look for custom element that might create
4357      the player (make some assumptions about the right custom element) */
4358   if (!local_player->present)
4359   {
4360     int start_x = 0, start_y = 0;
4361     int found_rating = 0;
4362     int found_element = EL_UNDEFINED;
4363     int player_nr = local_player->index_nr;
4364
4365     SCAN_PLAYFIELD(x, y)
4366     {
4367       int element = Feld[x][y];
4368       int content;
4369       int xx, yy;
4370       boolean is_player;
4371
4372       if (level.use_start_element[player_nr] &&
4373           level.start_element[player_nr] == element &&
4374           found_rating < 4)
4375       {
4376         start_x = x;
4377         start_y = y;
4378
4379         found_rating = 4;
4380         found_element = element;
4381       }
4382
4383       if (!IS_CUSTOM_ELEMENT(element))
4384         continue;
4385
4386       if (CAN_CHANGE(element))
4387       {
4388         for (i = 0; i < element_info[element].num_change_pages; i++)
4389         {
4390           /* check for player created from custom element as single target */
4391           content = element_info[element].change_page[i].target_element;
4392           is_player = ELEM_IS_PLAYER(content);
4393
4394           if (is_player && (found_rating < 3 ||
4395                             (found_rating == 3 && element < found_element)))
4396           {
4397             start_x = x;
4398             start_y = y;
4399
4400             found_rating = 3;
4401             found_element = element;
4402           }
4403         }
4404       }
4405
4406       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4407       {
4408         /* check for player created from custom element as explosion content */
4409         content = element_info[element].content.e[xx][yy];
4410         is_player = ELEM_IS_PLAYER(content);
4411
4412         if (is_player && (found_rating < 2 ||
4413                           (found_rating == 2 && element < found_element)))
4414         {
4415           start_x = x + xx - 1;
4416           start_y = y + yy - 1;
4417
4418           found_rating = 2;
4419           found_element = element;
4420         }
4421
4422         if (!CAN_CHANGE(element))
4423           continue;
4424
4425         for (i = 0; i < element_info[element].num_change_pages; i++)
4426         {
4427           /* check for player created from custom element as extended target */
4428           content =
4429             element_info[element].change_page[i].target_content.e[xx][yy];
4430
4431           is_player = ELEM_IS_PLAYER(content);
4432
4433           if (is_player && (found_rating < 1 ||
4434                             (found_rating == 1 && element < found_element)))
4435           {
4436             start_x = x + xx - 1;
4437             start_y = y + yy - 1;
4438
4439             found_rating = 1;
4440             found_element = element;
4441           }
4442         }
4443       }
4444     }
4445
4446     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4447                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4448                 start_x - MIDPOSX);
4449
4450     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4451                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4452                 start_y - MIDPOSY);
4453   }
4454   else
4455   {
4456     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4457                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4458                 local_player->jx - MIDPOSX);
4459
4460     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4461                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4462                 local_player->jy - MIDPOSY);
4463   }
4464
4465 #if 0
4466   /* do not use PLAYING mask for fading out from main screen */
4467   game_status = GAME_MODE_MAIN;
4468 #endif
4469
4470   StopAnimation();
4471
4472   if (!game.restart_level)
4473     CloseDoor(DOOR_CLOSE_1);
4474
4475 #if 1
4476   if (level_editor_test_game)
4477     FadeSkipNextFadeIn();
4478   else
4479     FadeSetEnterScreen();
4480 #else
4481   if (level_editor_test_game)
4482     fading = fading_none;
4483   else
4484     fading = menu.destination;
4485 #endif
4486
4487 #if 1
4488   FadeOut(REDRAW_FIELD);
4489 #else
4490   if (do_fading)
4491     FadeOut(REDRAW_FIELD);
4492 #endif
4493
4494 #if 0
4495   game_status = GAME_MODE_PLAYING;
4496 #endif
4497
4498   /* !!! FIX THIS (START) !!! */
4499   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4500   {
4501     InitGameEngine_EM();
4502
4503     /* blit playfield from scroll buffer to normal back buffer for fading in */
4504     BlitScreenToBitmap_EM(backbuffer);
4505   }
4506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4507   {
4508     InitGameEngine_SP();
4509
4510     /* blit playfield from scroll buffer to normal back buffer for fading in */
4511     BlitScreenToBitmap_SP(backbuffer);
4512   }
4513   else
4514   {
4515     DrawLevel();
4516     DrawAllPlayers();
4517
4518     /* after drawing the level, correct some elements */
4519     if (game.timegate_time_left == 0)
4520       CloseAllOpenTimegates();
4521
4522     /* blit playfield from scroll buffer to normal back buffer for fading in */
4523     if (setup.soft_scrolling)
4524       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4525
4526     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4527   }
4528   /* !!! FIX THIS (END) !!! */
4529
4530 #if 1
4531   FadeIn(REDRAW_FIELD);
4532 #else
4533   if (do_fading)
4534     FadeIn(REDRAW_FIELD);
4535
4536   BackToFront();
4537 #endif
4538
4539   if (!game.restart_level)
4540   {
4541     /* copy default game door content to main double buffer */
4542     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4543                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4544   }
4545
4546   SetPanelBackground();
4547   SetDrawBackgroundMask(REDRAW_DOOR_1);
4548
4549 #if 1
4550   UpdateAndDisplayGameControlValues();
4551 #else
4552   UpdateGameDoorValues();
4553   DrawGameDoorValues();
4554 #endif
4555
4556   if (!game.restart_level)
4557   {
4558     UnmapGameButtons();
4559     UnmapTapeButtons();
4560     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4561     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4562     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4563     MapGameButtons();
4564     MapTapeButtons();
4565
4566     /* copy actual game door content to door double buffer for OpenDoor() */
4567     BlitBitmap(drawto, bitmap_db_door,
4568                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4569
4570     OpenDoor(DOOR_OPEN_ALL);
4571
4572     PlaySound(SND_GAME_STARTING);
4573
4574     if (setup.sound_music)
4575       PlayLevelMusic();
4576
4577     KeyboardAutoRepeatOffUnlessAutoplay();
4578
4579     if (options.debug)
4580     {
4581       for (i = 0; i < MAX_PLAYERS; i++)
4582         printf("Player %d %sactive.\n",
4583                i + 1, (stored_player[i].active ? "" : "not "));
4584     }
4585   }
4586
4587 #if 1
4588   UnmapAllGadgets();
4589
4590   MapGameButtons();
4591   MapTapeButtons();
4592 #endif
4593
4594   game.restart_level = FALSE;
4595 }
4596
4597 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4598 {
4599   /* this is used for non-R'n'D game engines to update certain engine values */
4600
4601   /* needed to determine if sounds are played within the visible screen area */
4602   scroll_x = actual_scroll_x;
4603   scroll_y = actual_scroll_y;
4604 }
4605
4606 void InitMovDir(int x, int y)
4607 {
4608   int i, element = Feld[x][y];
4609   static int xy[4][2] =
4610   {
4611     {  0, +1 },
4612     { +1,  0 },
4613     {  0, -1 },
4614     { -1,  0 }
4615   };
4616   static int direction[3][4] =
4617   {
4618     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4619     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4620     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4621   };
4622
4623   switch (element)
4624   {
4625     case EL_BUG_RIGHT:
4626     case EL_BUG_UP:
4627     case EL_BUG_LEFT:
4628     case EL_BUG_DOWN:
4629       Feld[x][y] = EL_BUG;
4630       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4631       break;
4632
4633     case EL_SPACESHIP_RIGHT:
4634     case EL_SPACESHIP_UP:
4635     case EL_SPACESHIP_LEFT:
4636     case EL_SPACESHIP_DOWN:
4637       Feld[x][y] = EL_SPACESHIP;
4638       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4639       break;
4640
4641     case EL_BD_BUTTERFLY_RIGHT:
4642     case EL_BD_BUTTERFLY_UP:
4643     case EL_BD_BUTTERFLY_LEFT:
4644     case EL_BD_BUTTERFLY_DOWN:
4645       Feld[x][y] = EL_BD_BUTTERFLY;
4646       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4647       break;
4648
4649     case EL_BD_FIREFLY_RIGHT:
4650     case EL_BD_FIREFLY_UP:
4651     case EL_BD_FIREFLY_LEFT:
4652     case EL_BD_FIREFLY_DOWN:
4653       Feld[x][y] = EL_BD_FIREFLY;
4654       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4655       break;
4656
4657     case EL_PACMAN_RIGHT:
4658     case EL_PACMAN_UP:
4659     case EL_PACMAN_LEFT:
4660     case EL_PACMAN_DOWN:
4661       Feld[x][y] = EL_PACMAN;
4662       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4663       break;
4664
4665     case EL_YAMYAM_LEFT:
4666     case EL_YAMYAM_RIGHT:
4667     case EL_YAMYAM_UP:
4668     case EL_YAMYAM_DOWN:
4669       Feld[x][y] = EL_YAMYAM;
4670       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4671       break;
4672
4673     case EL_SP_SNIKSNAK:
4674       MovDir[x][y] = MV_UP;
4675       break;
4676
4677     case EL_SP_ELECTRON:
4678       MovDir[x][y] = MV_LEFT;
4679       break;
4680
4681     case EL_MOLE_LEFT:
4682     case EL_MOLE_RIGHT:
4683     case EL_MOLE_UP:
4684     case EL_MOLE_DOWN:
4685       Feld[x][y] = EL_MOLE;
4686       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4687       break;
4688
4689     default:
4690       if (IS_CUSTOM_ELEMENT(element))
4691       {
4692         struct ElementInfo *ei = &element_info[element];
4693         int move_direction_initial = ei->move_direction_initial;
4694         int move_pattern = ei->move_pattern;
4695
4696         if (move_direction_initial == MV_START_PREVIOUS)
4697         {
4698           if (MovDir[x][y] != MV_NONE)
4699             return;
4700
4701           move_direction_initial = MV_START_AUTOMATIC;
4702         }
4703
4704         if (move_direction_initial == MV_START_RANDOM)
4705           MovDir[x][y] = 1 << RND(4);
4706         else if (move_direction_initial & MV_ANY_DIRECTION)
4707           MovDir[x][y] = move_direction_initial;
4708         else if (move_pattern == MV_ALL_DIRECTIONS ||
4709                  move_pattern == MV_TURNING_LEFT ||
4710                  move_pattern == MV_TURNING_RIGHT ||
4711                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4712                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4713                  move_pattern == MV_TURNING_RANDOM)
4714           MovDir[x][y] = 1 << RND(4);
4715         else if (move_pattern == MV_HORIZONTAL)
4716           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4717         else if (move_pattern == MV_VERTICAL)
4718           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4719         else if (move_pattern & MV_ANY_DIRECTION)
4720           MovDir[x][y] = element_info[element].move_pattern;
4721         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4722                  move_pattern == MV_ALONG_RIGHT_SIDE)
4723         {
4724           /* use random direction as default start direction */
4725           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4726             MovDir[x][y] = 1 << RND(4);
4727
4728           for (i = 0; i < NUM_DIRECTIONS; i++)
4729           {
4730             int x1 = x + xy[i][0];
4731             int y1 = y + xy[i][1];
4732
4733             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4734             {
4735               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4736                 MovDir[x][y] = direction[0][i];
4737               else
4738                 MovDir[x][y] = direction[1][i];
4739
4740               break;
4741             }
4742           }
4743         }                
4744       }
4745       else
4746       {
4747         MovDir[x][y] = 1 << RND(4);
4748
4749         if (element != EL_BUG &&
4750             element != EL_SPACESHIP &&
4751             element != EL_BD_BUTTERFLY &&
4752             element != EL_BD_FIREFLY)
4753           break;
4754
4755         for (i = 0; i < NUM_DIRECTIONS; i++)
4756         {
4757           int x1 = x + xy[i][0];
4758           int y1 = y + xy[i][1];
4759
4760           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4761           {
4762             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4763             {
4764               MovDir[x][y] = direction[0][i];
4765               break;
4766             }
4767             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4768                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4769             {
4770               MovDir[x][y] = direction[1][i];
4771               break;
4772             }
4773           }
4774         }
4775       }
4776       break;
4777   }
4778
4779   GfxDir[x][y] = MovDir[x][y];
4780 }
4781
4782 void InitAmoebaNr(int x, int y)
4783 {
4784   int i;
4785   int group_nr = AmoebeNachbarNr(x, y);
4786
4787   if (group_nr == 0)
4788   {
4789     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4790     {
4791       if (AmoebaCnt[i] == 0)
4792       {
4793         group_nr = i;
4794         break;
4795       }
4796     }
4797   }
4798
4799   AmoebaNr[x][y] = group_nr;
4800   AmoebaCnt[group_nr]++;
4801   AmoebaCnt2[group_nr]++;
4802 }
4803
4804 static void PlayerWins(struct PlayerInfo *player)
4805 {
4806   player->LevelSolved = TRUE;
4807   player->GameOver = TRUE;
4808
4809   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4810                          level.native_em_level->lev->score : player->score);
4811
4812   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4813   player->LevelSolved_CountingScore = player->score_final;
4814 }
4815
4816 void GameWon()
4817 {
4818   static int time, time_final;
4819   static int score, score_final;
4820   static int game_over_delay_1 = 0;
4821   static int game_over_delay_2 = 0;
4822   int game_over_delay_value_1 = 50;
4823   int game_over_delay_value_2 = 50;
4824
4825   if (!local_player->LevelSolved_GameWon)
4826   {
4827     int i;
4828
4829     /* do not start end game actions before the player stops moving (to exit) */
4830     if (local_player->MovPos)
4831       return;
4832
4833     local_player->LevelSolved_GameWon = TRUE;
4834     local_player->LevelSolved_SaveTape = tape.recording;
4835     local_player->LevelSolved_SaveScore = !tape.playing;
4836
4837     if (tape.auto_play)         /* tape might already be stopped here */
4838       tape.auto_play_level_solved = TRUE;
4839
4840 #if 1
4841     TapeStop();
4842 #endif
4843
4844     game_over_delay_1 = game_over_delay_value_1;
4845     game_over_delay_2 = game_over_delay_value_2;
4846
4847     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4848     score = score_final = local_player->score_final;
4849
4850     if (TimeLeft > 0)
4851     {
4852       time_final = 0;
4853       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4854     }
4855     else if (level.time == 0 && TimePlayed < 999)
4856     {
4857       time_final = 999;
4858       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4859     }
4860
4861     local_player->score_final = score_final;
4862
4863     if (level_editor_test_game)
4864     {
4865       time = time_final;
4866       score = score_final;
4867
4868 #if 1
4869       local_player->LevelSolved_CountingTime = time;
4870       local_player->LevelSolved_CountingScore = score;
4871
4872       game_panel_controls[GAME_PANEL_TIME].value = time;
4873       game_panel_controls[GAME_PANEL_SCORE].value = score;
4874
4875       DisplayGameControlValues();
4876 #else
4877       DrawGameValue_Time(time);
4878       DrawGameValue_Score(score);
4879 #endif
4880     }
4881
4882     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4883     {
4884       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4885       {
4886         /* close exit door after last player */
4887         if ((AllPlayersGone &&
4888              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4889               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4890               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4891             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4892             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4893         {
4894           int element = Feld[ExitX][ExitY];
4895
4896 #if 0
4897           if (element == EL_EM_EXIT_OPEN ||
4898               element == EL_EM_STEEL_EXIT_OPEN)
4899           {
4900             Bang(ExitX, ExitY);
4901           }
4902           else
4903 #endif
4904           {
4905             Feld[ExitX][ExitY] =
4906               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4907                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4908                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4909                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4910                EL_EM_STEEL_EXIT_CLOSING);
4911
4912             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4913           }
4914         }
4915
4916         /* player disappears */
4917         DrawLevelField(ExitX, ExitY);
4918       }
4919
4920       for (i = 0; i < MAX_PLAYERS; i++)
4921       {
4922         struct PlayerInfo *player = &stored_player[i];
4923
4924         if (player->present)
4925         {
4926           RemovePlayer(player);
4927
4928           /* player disappears */
4929           DrawLevelField(player->jx, player->jy);
4930         }
4931       }
4932     }
4933
4934     PlaySound(SND_GAME_WINNING);
4935   }
4936
4937   if (game_over_delay_1 > 0)
4938   {
4939     game_over_delay_1--;
4940
4941     return;
4942   }
4943
4944   if (time != time_final)
4945   {
4946     int time_to_go = ABS(time_final - time);
4947     int time_count_dir = (time < time_final ? +1 : -1);
4948     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4949
4950     time  += time_count_steps * time_count_dir;
4951     score += time_count_steps * level.score[SC_TIME_BONUS];
4952
4953 #if 1
4954     local_player->LevelSolved_CountingTime = time;
4955     local_player->LevelSolved_CountingScore = score;
4956
4957     game_panel_controls[GAME_PANEL_TIME].value = time;
4958     game_panel_controls[GAME_PANEL_SCORE].value = score;
4959
4960     DisplayGameControlValues();
4961 #else
4962     DrawGameValue_Time(time);
4963     DrawGameValue_Score(score);
4964 #endif
4965
4966     if (time == time_final)
4967       StopSound(SND_GAME_LEVELTIME_BONUS);
4968     else if (setup.sound_loops)
4969       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4970     else
4971       PlaySound(SND_GAME_LEVELTIME_BONUS);
4972
4973     return;
4974   }
4975
4976   local_player->LevelSolved_PanelOff = TRUE;
4977
4978   if (game_over_delay_2 > 0)
4979   {
4980     game_over_delay_2--;
4981
4982     return;
4983   }
4984
4985 #if 1
4986   GameEnd();
4987 #endif
4988 }
4989
4990 void GameEnd()
4991 {
4992   int hi_pos;
4993   boolean raise_level = FALSE;
4994
4995   local_player->LevelSolved_GameEnd = TRUE;
4996
4997   CloseDoor(DOOR_CLOSE_1);
4998
4999   if (local_player->LevelSolved_SaveTape)
5000   {
5001 #if 0
5002     TapeStop();
5003 #endif
5004
5005 #if 1
5006     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5007 #else
5008     SaveTape(tape.level_nr);            /* ask to save tape */
5009 #endif
5010   }
5011
5012   if (level_editor_test_game)
5013   {
5014     game_status = GAME_MODE_MAIN;
5015
5016 #if 1
5017     DrawAndFadeInMainMenu(REDRAW_FIELD);
5018 #else
5019     DrawMainMenu();
5020 #endif
5021
5022     return;
5023   }
5024
5025   if (!local_player->LevelSolved_SaveScore)
5026   {
5027 #if 1
5028     FadeOut(REDRAW_FIELD);
5029 #endif
5030
5031     game_status = GAME_MODE_MAIN;
5032
5033     DrawAndFadeInMainMenu(REDRAW_FIELD);
5034
5035     return;
5036   }
5037
5038   if (level_nr == leveldir_current->handicap_level)
5039   {
5040     leveldir_current->handicap_level++;
5041     SaveLevelSetup_SeriesInfo();
5042   }
5043
5044   if (level_nr < leveldir_current->last_level)
5045     raise_level = TRUE;                 /* advance to next level */
5046
5047   if ((hi_pos = NewHiScore()) >= 0) 
5048   {
5049     game_status = GAME_MODE_SCORES;
5050
5051     DrawHallOfFame(hi_pos);
5052
5053     if (raise_level)
5054     {
5055       level_nr++;
5056       TapeErase();
5057     }
5058   }
5059   else
5060   {
5061 #if 1
5062     FadeOut(REDRAW_FIELD);
5063 #endif
5064
5065     game_status = GAME_MODE_MAIN;
5066
5067     if (raise_level)
5068     {
5069       level_nr++;
5070       TapeErase();
5071     }
5072
5073     DrawAndFadeInMainMenu(REDRAW_FIELD);
5074   }
5075 }
5076
5077 int NewHiScore()
5078 {
5079   int k, l;
5080   int position = -1;
5081
5082   LoadScore(level_nr);
5083
5084   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5085       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5086     return -1;
5087
5088   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5089   {
5090     if (local_player->score_final > highscore[k].Score)
5091     {
5092       /* player has made it to the hall of fame */
5093
5094       if (k < MAX_SCORE_ENTRIES - 1)
5095       {
5096         int m = MAX_SCORE_ENTRIES - 1;
5097
5098 #ifdef ONE_PER_NAME
5099         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5100           if (strEqual(setup.player_name, highscore[l].Name))
5101             m = l;
5102         if (m == k)     /* player's new highscore overwrites his old one */
5103           goto put_into_list;
5104 #endif
5105
5106         for (l = m; l > k; l--)
5107         {
5108           strcpy(highscore[l].Name, highscore[l - 1].Name);
5109           highscore[l].Score = highscore[l - 1].Score;
5110         }
5111       }
5112
5113 #ifdef ONE_PER_NAME
5114       put_into_list:
5115 #endif
5116       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5117       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5118       highscore[k].Score = local_player->score_final; 
5119       position = k;
5120       break;
5121     }
5122
5123 #ifdef ONE_PER_NAME
5124     else if (!strncmp(setup.player_name, highscore[k].Name,
5125                       MAX_PLAYER_NAME_LEN))
5126       break;    /* player already there with a higher score */
5127 #endif
5128
5129   }
5130
5131   if (position >= 0) 
5132     SaveScore(level_nr);
5133
5134   return position;
5135 }
5136
5137 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5138 {
5139   int element = Feld[x][y];
5140   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5141   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5142   int horiz_move = (dx != 0);
5143   int sign = (horiz_move ? dx : dy);
5144   int step = sign * element_info[element].move_stepsize;
5145
5146   /* special values for move stepsize for spring and things on conveyor belt */
5147   if (horiz_move)
5148   {
5149     if (CAN_FALL(element) &&
5150         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5151       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5152     else if (element == EL_SPRING)
5153       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5154   }
5155
5156   return step;
5157 }
5158
5159 inline static int getElementMoveStepsize(int x, int y)
5160 {
5161   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5162 }
5163
5164 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5165 {
5166   if (player->GfxAction != action || player->GfxDir != dir)
5167   {
5168 #if 0
5169     printf("Player frame reset! (%d => %d, %d => %d)\n",
5170            player->GfxAction, action, player->GfxDir, dir);
5171 #endif
5172
5173     player->GfxAction = action;
5174     player->GfxDir = dir;
5175     player->Frame = 0;
5176     player->StepFrame = 0;
5177   }
5178 }
5179
5180 #if USE_GFX_RESET_GFX_ANIMATION
5181 static void ResetGfxFrame(int x, int y, boolean redraw)
5182 {
5183   int element = Feld[x][y];
5184   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5185   int last_gfx_frame = GfxFrame[x][y];
5186
5187   if (graphic_info[graphic].anim_global_sync)
5188     GfxFrame[x][y] = FrameCounter;
5189   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5190     GfxFrame[x][y] = CustomValue[x][y];
5191   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5192     GfxFrame[x][y] = element_info[element].collect_score;
5193   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5194     GfxFrame[x][y] = ChangeDelay[x][y];
5195
5196   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5197     DrawLevelGraphicAnimation(x, y, graphic);
5198 }
5199 #endif
5200
5201 static void ResetGfxAnimation(int x, int y)
5202 {
5203   GfxAction[x][y] = ACTION_DEFAULT;
5204   GfxDir[x][y] = MovDir[x][y];
5205   GfxFrame[x][y] = 0;
5206
5207 #if USE_GFX_RESET_GFX_ANIMATION
5208   ResetGfxFrame(x, y, FALSE);
5209 #endif
5210 }
5211
5212 static void ResetRandomAnimationValue(int x, int y)
5213 {
5214   GfxRandom[x][y] = INIT_GFX_RANDOM();
5215 }
5216
5217 void InitMovingField(int x, int y, int direction)
5218 {
5219   int element = Feld[x][y];
5220   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5221   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5222   int newx = x + dx;
5223   int newy = y + dy;
5224   boolean is_moving_before, is_moving_after;
5225 #if 0
5226   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5227 #endif
5228
5229   /* check if element was/is moving or being moved before/after mode change */
5230 #if 1
5231 #if 1
5232   is_moving_before = (WasJustMoving[x][y] != 0);
5233 #else
5234   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5235   is_moving_before = WasJustMoving[x][y];
5236 #endif
5237 #else
5238   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5239 #endif
5240   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5241
5242   /* reset animation only for moving elements which change direction of moving
5243      or which just started or stopped moving
5244      (else CEs with property "can move" / "not moving" are reset each frame) */
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5246 #if 1
5247   if (is_moving_before != is_moving_after ||
5248       direction != MovDir[x][y])
5249     ResetGfxAnimation(x, y);
5250 #else
5251   if ((is_moving_before || is_moving_after) && !continues_moving)
5252     ResetGfxAnimation(x, y);
5253 #endif
5254 #else
5255   if (!continues_moving)
5256     ResetGfxAnimation(x, y);
5257 #endif
5258
5259   MovDir[x][y] = direction;
5260   GfxDir[x][y] = direction;
5261
5262 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5263   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5264                      direction == MV_DOWN && CAN_FALL(element) ?
5265                      ACTION_FALLING : ACTION_MOVING);
5266 #else
5267   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5268                      ACTION_FALLING : ACTION_MOVING);
5269 #endif
5270
5271   /* this is needed for CEs with property "can move" / "not moving" */
5272
5273   if (is_moving_after)
5274   {
5275     if (Feld[newx][newy] == EL_EMPTY)
5276       Feld[newx][newy] = EL_BLOCKED;
5277
5278     MovDir[newx][newy] = MovDir[x][y];
5279
5280 #if USE_NEW_CUSTOM_VALUE
5281     CustomValue[newx][newy] = CustomValue[x][y];
5282 #endif
5283
5284     GfxFrame[newx][newy] = GfxFrame[x][y];
5285     GfxRandom[newx][newy] = GfxRandom[x][y];
5286     GfxAction[newx][newy] = GfxAction[x][y];
5287     GfxDir[newx][newy] = GfxDir[x][y];
5288   }
5289 }
5290
5291 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5292 {
5293   int direction = MovDir[x][y];
5294   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5295   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5296
5297   *goes_to_x = newx;
5298   *goes_to_y = newy;
5299 }
5300
5301 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5302 {
5303   int oldx = x, oldy = y;
5304   int direction = MovDir[x][y];
5305
5306   if (direction == MV_LEFT)
5307     oldx++;
5308   else if (direction == MV_RIGHT)
5309     oldx--;
5310   else if (direction == MV_UP)
5311     oldy++;
5312   else if (direction == MV_DOWN)
5313     oldy--;
5314
5315   *comes_from_x = oldx;
5316   *comes_from_y = oldy;
5317 }
5318
5319 int MovingOrBlocked2Element(int x, int y)
5320 {
5321   int element = Feld[x][y];
5322
5323   if (element == EL_BLOCKED)
5324   {
5325     int oldx, oldy;
5326
5327     Blocked2Moving(x, y, &oldx, &oldy);
5328     return Feld[oldx][oldy];
5329   }
5330   else
5331     return element;
5332 }
5333
5334 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5335 {
5336   /* like MovingOrBlocked2Element(), but if element is moving
5337      and (x,y) is the field the moving element is just leaving,
5338      return EL_BLOCKED instead of the element value */
5339   int element = Feld[x][y];
5340
5341   if (IS_MOVING(x, y))
5342   {
5343     if (element == EL_BLOCKED)
5344     {
5345       int oldx, oldy;
5346
5347       Blocked2Moving(x, y, &oldx, &oldy);
5348       return Feld[oldx][oldy];
5349     }
5350     else
5351       return EL_BLOCKED;
5352   }
5353   else
5354     return element;
5355 }
5356
5357 static void RemoveField(int x, int y)
5358 {
5359   Feld[x][y] = EL_EMPTY;
5360
5361   MovPos[x][y] = 0;
5362   MovDir[x][y] = 0;
5363   MovDelay[x][y] = 0;
5364
5365 #if USE_NEW_CUSTOM_VALUE
5366   CustomValue[x][y] = 0;
5367 #endif
5368
5369   AmoebaNr[x][y] = 0;
5370   ChangeDelay[x][y] = 0;
5371   ChangePage[x][y] = -1;
5372   Pushed[x][y] = FALSE;
5373
5374 #if 0
5375   ExplodeField[x][y] = EX_TYPE_NONE;
5376 #endif
5377
5378   GfxElement[x][y] = EL_UNDEFINED;
5379   GfxAction[x][y] = ACTION_DEFAULT;
5380   GfxDir[x][y] = MV_NONE;
5381 #if 0
5382   /* !!! this would prevent the removed tile from being redrawn !!! */
5383   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5384 #endif
5385 }
5386
5387 void RemoveMovingField(int x, int y)
5388 {
5389   int oldx = x, oldy = y, newx = x, newy = y;
5390   int element = Feld[x][y];
5391   int next_element = EL_UNDEFINED;
5392
5393   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5394     return;
5395
5396   if (IS_MOVING(x, y))
5397   {
5398     Moving2Blocked(x, y, &newx, &newy);
5399
5400     if (Feld[newx][newy] != EL_BLOCKED)
5401     {
5402       /* element is moving, but target field is not free (blocked), but
5403          already occupied by something different (example: acid pool);
5404          in this case, only remove the moving field, but not the target */
5405
5406       RemoveField(oldx, oldy);
5407
5408       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5409
5410       TEST_DrawLevelField(oldx, oldy);
5411
5412       return;
5413     }
5414   }
5415   else if (element == EL_BLOCKED)
5416   {
5417     Blocked2Moving(x, y, &oldx, &oldy);
5418     if (!IS_MOVING(oldx, oldy))
5419       return;
5420   }
5421
5422   if (element == EL_BLOCKED &&
5423       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5424        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5425        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5426        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5427        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5428        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5429     next_element = get_next_element(Feld[oldx][oldy]);
5430
5431   RemoveField(oldx, oldy);
5432   RemoveField(newx, newy);
5433
5434   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5435
5436   if (next_element != EL_UNDEFINED)
5437     Feld[oldx][oldy] = next_element;
5438
5439   TEST_DrawLevelField(oldx, oldy);
5440   TEST_DrawLevelField(newx, newy);
5441 }
5442
5443 void DrawDynamite(int x, int y)
5444 {
5445   int sx = SCREENX(x), sy = SCREENY(y);
5446   int graphic = el2img(Feld[x][y]);
5447   int frame;
5448
5449   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5450     return;
5451
5452   if (IS_WALKABLE_INSIDE(Back[x][y]))
5453     return;
5454
5455   if (Back[x][y])
5456     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5457   else if (Store[x][y])
5458     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5459
5460   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5461
5462   if (Back[x][y] || Store[x][y])
5463     DrawGraphicThruMask(sx, sy, graphic, frame);
5464   else
5465     DrawGraphic(sx, sy, graphic, frame);
5466 }
5467
5468 void CheckDynamite(int x, int y)
5469 {
5470   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5471   {
5472     MovDelay[x][y]--;
5473
5474     if (MovDelay[x][y] != 0)
5475     {
5476       DrawDynamite(x, y);
5477       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5478
5479       return;
5480     }
5481   }
5482
5483   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5484
5485   Bang(x, y);
5486 }
5487
5488 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5489 {
5490   boolean num_checked_players = 0;
5491   int i;
5492
5493   for (i = 0; i < MAX_PLAYERS; i++)
5494   {
5495     if (stored_player[i].active)
5496     {
5497       int sx = stored_player[i].jx;
5498       int sy = stored_player[i].jy;
5499
5500       if (num_checked_players == 0)
5501       {
5502         *sx1 = *sx2 = sx;
5503         *sy1 = *sy2 = sy;
5504       }
5505       else
5506       {
5507         *sx1 = MIN(*sx1, sx);
5508         *sy1 = MIN(*sy1, sy);
5509         *sx2 = MAX(*sx2, sx);
5510         *sy2 = MAX(*sy2, sy);
5511       }
5512
5513       num_checked_players++;
5514     }
5515   }
5516 }
5517
5518 static boolean checkIfAllPlayersFitToScreen_RND()
5519 {
5520   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5521
5522   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5523
5524   return (sx2 - sx1 < SCR_FIELDX &&
5525           sy2 - sy1 < SCR_FIELDY);
5526 }
5527
5528 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5529 {
5530   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5531
5532   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5533
5534   *sx = (sx1 + sx2) / 2;
5535   *sy = (sy1 + sy2) / 2;
5536 }
5537
5538 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5539                         boolean center_screen, boolean quick_relocation)
5540 {
5541   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5542   boolean no_delay = (tape.warp_forward);
5543   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5544   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5545
5546   if (quick_relocation)
5547   {
5548     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5549     {
5550       if (!level.shifted_relocation || center_screen)
5551       {
5552         /* quick relocation (without scrolling), with centering of screen */
5553
5554         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5555                     x > SBX_Right + MIDPOSX ? SBX_Right :
5556                     x - MIDPOSX);
5557
5558         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5559                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5560                     y - MIDPOSY);
5561       }
5562       else
5563       {
5564         /* quick relocation (without scrolling), but do not center screen */
5565
5566         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5567                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5568                                old_x - MIDPOSX);
5569
5570         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5572                                old_y - MIDPOSY);
5573
5574         int offset_x = x + (scroll_x - center_scroll_x);
5575         int offset_y = y + (scroll_y - center_scroll_y);
5576
5577         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5578                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5579                     offset_x - MIDPOSX);
5580
5581         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583                     offset_y - MIDPOSY);
5584       }
5585     }
5586     else
5587     {
5588 #if 1
5589       if (!level.shifted_relocation || center_screen)
5590       {
5591         /* quick relocation (without scrolling), with centering of screen */
5592
5593         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5594                     x > SBX_Right + MIDPOSX ? SBX_Right :
5595                     x - MIDPOSX);
5596
5597         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5598                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5599                     y - MIDPOSY);
5600       }
5601       else
5602       {
5603         /* quick relocation (without scrolling), but do not center screen */
5604
5605         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5606                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5607                                old_x - MIDPOSX);
5608
5609         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5611                                old_y - MIDPOSY);
5612
5613         int offset_x = x + (scroll_x - center_scroll_x);
5614         int offset_y = y + (scroll_y - center_scroll_y);
5615
5616         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5617                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5618                     offset_x - MIDPOSX);
5619
5620         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622                     offset_y - MIDPOSY);
5623       }
5624 #else
5625       /* quick relocation (without scrolling), inside visible screen area */
5626
5627       int offset = game.scroll_delay_value;
5628
5629       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5630           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5631         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5632
5633       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5634           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5635         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5636
5637       /* don't scroll over playfield boundaries */
5638       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5639         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5640
5641       /* don't scroll over playfield boundaries */
5642       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5643         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5644 #endif
5645     }
5646
5647     RedrawPlayfield(TRUE, 0,0,0,0);
5648   }
5649   else
5650   {
5651 #if 1
5652     int scroll_xx, scroll_yy;
5653
5654     if (!level.shifted_relocation || center_screen)
5655     {
5656       /* visible relocation (with scrolling), with centering of screen */
5657
5658       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5659                    x > SBX_Right + MIDPOSX ? SBX_Right :
5660                    x - MIDPOSX);
5661
5662       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5663                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5664                    y - MIDPOSY);
5665     }
5666     else
5667     {
5668       /* visible relocation (with scrolling), but do not center screen */
5669
5670       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5671                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5672                              old_x - MIDPOSX);
5673
5674       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5676                              old_y - MIDPOSY);
5677
5678       int offset_x = x + (scroll_x - center_scroll_x);
5679       int offset_y = y + (scroll_y - center_scroll_y);
5680
5681       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5682                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5683                    offset_x - MIDPOSX);
5684
5685       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5687                    offset_y - MIDPOSY);
5688     }
5689
5690 #else
5691
5692     /* visible relocation (with scrolling), with centering of screen */
5693
5694     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5695                      x > SBX_Right + MIDPOSX ? SBX_Right :
5696                      x - MIDPOSX);
5697
5698     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5699                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5700                      y - MIDPOSY);
5701 #endif
5702
5703     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5704
5705     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5706     {
5707       int dx = 0, dy = 0;
5708       int fx = FX, fy = FY;
5709
5710       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5711       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5712
5713       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5714         break;
5715
5716       scroll_x -= dx;
5717       scroll_y -= dy;
5718
5719       fx += dx * TILEX / 2;
5720       fy += dy * TILEY / 2;
5721
5722       ScrollLevel(dx, dy);
5723       DrawAllPlayers();
5724
5725       /* scroll in two steps of half tile size to make things smoother */
5726       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5727       FlushDisplay();
5728       Delay(wait_delay_value);
5729
5730       /* scroll second step to align at full tile size */
5731       BackToFront();
5732       Delay(wait_delay_value);
5733     }
5734
5735     DrawAllPlayers();
5736     BackToFront();
5737     Delay(wait_delay_value);
5738   }
5739 }
5740
5741 void RelocatePlayer(int jx, int jy, int el_player_raw)
5742 {
5743   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5744   int player_nr = GET_PLAYER_NR(el_player);
5745   struct PlayerInfo *player = &stored_player[player_nr];
5746   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5747   boolean no_delay = (tape.warp_forward);
5748   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5749   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5750   int old_jx = player->jx;
5751   int old_jy = player->jy;
5752   int old_element = Feld[old_jx][old_jy];
5753   int element = Feld[jx][jy];
5754   boolean player_relocated = (old_jx != jx || old_jy != jy);
5755
5756   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5757   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5758   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5759   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5760   int leave_side_horiz = move_dir_horiz;
5761   int leave_side_vert  = move_dir_vert;
5762   int enter_side = enter_side_horiz | enter_side_vert;
5763   int leave_side = leave_side_horiz | leave_side_vert;
5764
5765   if (player->GameOver)         /* do not reanimate dead player */
5766     return;
5767
5768   if (!player_relocated)        /* no need to relocate the player */
5769     return;
5770
5771   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5772   {
5773     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5774     DrawLevelField(jx, jy);
5775   }
5776
5777   if (player->present)
5778   {
5779     while (player->MovPos)
5780     {
5781       ScrollPlayer(player, SCROLL_GO_ON);
5782       ScrollScreen(NULL, SCROLL_GO_ON);
5783
5784       AdvanceFrameAndPlayerCounters(player->index_nr);
5785
5786       DrawPlayer(player);
5787
5788       BackToFront();
5789       Delay(wait_delay_value);
5790     }
5791
5792     DrawPlayer(player);         /* needed here only to cleanup last field */
5793     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5794
5795     player->is_moving = FALSE;
5796   }
5797
5798   if (IS_CUSTOM_ELEMENT(old_element))
5799     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5800                                CE_LEFT_BY_PLAYER,
5801                                player->index_bit, leave_side);
5802
5803   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5804                                       CE_PLAYER_LEAVES_X,
5805                                       player->index_bit, leave_side);
5806
5807   Feld[jx][jy] = el_player;
5808   InitPlayerField(jx, jy, el_player, TRUE);
5809
5810   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5811      possible that the relocation target field did not contain a player element,
5812      but a walkable element, to which the new player was relocated -- in this
5813      case, restore that (already initialized!) element on the player field */
5814   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5815   {
5816     Feld[jx][jy] = element;     /* restore previously existing element */
5817 #if 0
5818     /* !!! do not initialize already initialized element a second time !!! */
5819     /* (this causes at least problems with "element creation" CE trigger for
5820        already existing elements, and existing Sokoban fields counted twice) */
5821     InitField(jx, jy, FALSE);
5822 #endif
5823   }
5824
5825   /* only visually relocate centered player */
5826   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5827                      FALSE, level.instant_relocation);
5828
5829   TestIfPlayerTouchesBadThing(jx, jy);
5830   TestIfPlayerTouchesCustomElement(jx, jy);
5831
5832   if (IS_CUSTOM_ELEMENT(element))
5833     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5834                                player->index_bit, enter_side);
5835
5836   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5837                                       player->index_bit, enter_side);
5838
5839 #if 1
5840   if (player->is_switching)
5841   {
5842     /* ensure that relocation while still switching an element does not cause
5843        a new element to be treated as also switched directly after relocation
5844        (this is important for teleporter switches that teleport the player to
5845        a place where another teleporter switch is in the same direction, which
5846        would then incorrectly be treated as immediately switched before the
5847        direction key that caused the switch was released) */
5848
5849     player->switch_x += jx - old_jx;
5850     player->switch_y += jy - old_jy;
5851   }
5852 #endif
5853 }
5854
5855 void Explode(int ex, int ey, int phase, int mode)
5856 {
5857   int x, y;
5858   int last_phase;
5859   int border_element;
5860
5861   /* !!! eliminate this variable !!! */
5862   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5863
5864   if (game.explosions_delayed)
5865   {
5866     ExplodeField[ex][ey] = mode;
5867     return;
5868   }
5869
5870   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5871   {
5872     int center_element = Feld[ex][ey];
5873     int artwork_element, explosion_element;     /* set these values later */
5874
5875 #if 0
5876     /* --- This is only really needed (and now handled) in "Impact()". --- */
5877     /* do not explode moving elements that left the explode field in time */
5878     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5879         center_element == EL_EMPTY &&
5880         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5881       return;
5882 #endif
5883
5884 #if 0
5885     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5886     if (mode == EX_TYPE_NORMAL ||
5887         mode == EX_TYPE_CENTER ||
5888         mode == EX_TYPE_CROSS)
5889       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5890 #endif
5891
5892     /* remove things displayed in background while burning dynamite */
5893     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5894       Back[ex][ey] = 0;
5895
5896     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5897     {
5898       /* put moving element to center field (and let it explode there) */
5899       center_element = MovingOrBlocked2Element(ex, ey);
5900       RemoveMovingField(ex, ey);
5901       Feld[ex][ey] = center_element;
5902     }
5903
5904     /* now "center_element" is finally determined -- set related values now */
5905     artwork_element = center_element;           /* for custom player artwork */
5906     explosion_element = center_element;         /* for custom player artwork */
5907
5908     if (IS_PLAYER(ex, ey))
5909     {
5910       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5911
5912       artwork_element = stored_player[player_nr].artwork_element;
5913
5914       if (level.use_explosion_element[player_nr])
5915       {
5916         explosion_element = level.explosion_element[player_nr];
5917         artwork_element = explosion_element;
5918       }
5919     }
5920
5921 #if 1
5922     if (mode == EX_TYPE_NORMAL ||
5923         mode == EX_TYPE_CENTER ||
5924         mode == EX_TYPE_CROSS)
5925       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5926 #endif
5927
5928     last_phase = element_info[explosion_element].explosion_delay + 1;
5929
5930     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5931     {
5932       int xx = x - ex + 1;
5933       int yy = y - ey + 1;
5934       int element;
5935
5936       if (!IN_LEV_FIELD(x, y) ||
5937           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5938           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5939         continue;
5940
5941       element = Feld[x][y];
5942
5943       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5944       {
5945         element = MovingOrBlocked2Element(x, y);
5946
5947         if (!IS_EXPLOSION_PROOF(element))
5948           RemoveMovingField(x, y);
5949       }
5950
5951       /* indestructible elements can only explode in center (but not flames) */
5952       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5953                                            mode == EX_TYPE_BORDER)) ||
5954           element == EL_FLAMES)
5955         continue;
5956
5957       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5958          behaviour, for example when touching a yamyam that explodes to rocks
5959          with active deadly shield, a rock is created under the player !!! */
5960       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5961 #if 0
5962       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5963           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5964            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5965 #else
5966       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5967 #endif
5968       {
5969         if (IS_ACTIVE_BOMB(element))
5970         {
5971           /* re-activate things under the bomb like gate or penguin */
5972           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5973           Back[x][y] = 0;
5974         }
5975
5976         continue;
5977       }
5978
5979       /* save walkable background elements while explosion on same tile */
5980       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5981           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5982         Back[x][y] = element;
5983
5984       /* ignite explodable elements reached by other explosion */
5985       if (element == EL_EXPLOSION)
5986         element = Store2[x][y];
5987
5988       if (AmoebaNr[x][y] &&
5989           (element == EL_AMOEBA_FULL ||
5990            element == EL_BD_AMOEBA ||
5991            element == EL_AMOEBA_GROWING))
5992       {
5993         AmoebaCnt[AmoebaNr[x][y]]--;
5994         AmoebaCnt2[AmoebaNr[x][y]]--;
5995       }
5996
5997       RemoveField(x, y);
5998
5999       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6000       {
6001         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6002
6003         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6004
6005         if (PLAYERINFO(ex, ey)->use_murphy)
6006           Store[x][y] = EL_EMPTY;
6007       }
6008
6009       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6010          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6011       else if (ELEM_IS_PLAYER(center_element))
6012         Store[x][y] = EL_EMPTY;
6013       else if (center_element == EL_YAMYAM)
6014         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6015       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6016         Store[x][y] = element_info[center_element].content.e[xx][yy];
6017 #if 1
6018       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6019          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6020          otherwise) -- FIX THIS !!! */
6021       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6022         Store[x][y] = element_info[element].content.e[1][1];
6023 #else
6024       else if (!CAN_EXPLODE(element))
6025         Store[x][y] = element_info[element].content.e[1][1];
6026 #endif
6027       else
6028         Store[x][y] = EL_EMPTY;
6029
6030       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6031           center_element == EL_AMOEBA_TO_DIAMOND)
6032         Store2[x][y] = element;
6033
6034       Feld[x][y] = EL_EXPLOSION;
6035       GfxElement[x][y] = artwork_element;
6036
6037       ExplodePhase[x][y] = 1;
6038       ExplodeDelay[x][y] = last_phase;
6039
6040       Stop[x][y] = TRUE;
6041     }
6042
6043     if (center_element == EL_YAMYAM)
6044       game.yamyam_content_nr =
6045         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6046
6047     return;
6048   }
6049
6050   if (Stop[ex][ey])
6051     return;
6052
6053   x = ex;
6054   y = ey;
6055
6056   if (phase == 1)
6057     GfxFrame[x][y] = 0;         /* restart explosion animation */
6058
6059   last_phase = ExplodeDelay[x][y];
6060
6061   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6062
6063 #ifdef DEBUG
6064
6065   /* activate this even in non-DEBUG version until cause for crash in
6066      getGraphicAnimationFrame() (see below) is found and eliminated */
6067
6068 #endif
6069 #if 1
6070
6071 #if 1
6072   /* this can happen if the player leaves an explosion just in time */
6073   if (GfxElement[x][y] == EL_UNDEFINED)
6074     GfxElement[x][y] = EL_EMPTY;
6075 #else
6076   if (GfxElement[x][y] == EL_UNDEFINED)
6077   {
6078     printf("\n\n");
6079     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6080     printf("Explode(): This should never happen!\n");
6081     printf("\n\n");
6082
6083     GfxElement[x][y] = EL_EMPTY;
6084   }
6085 #endif
6086
6087 #endif
6088
6089   border_element = Store2[x][y];
6090   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6091     border_element = StorePlayer[x][y];
6092
6093   if (phase == element_info[border_element].ignition_delay ||
6094       phase == last_phase)
6095   {
6096     boolean border_explosion = FALSE;
6097
6098     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6099         !PLAYER_EXPLOSION_PROTECTED(x, y))
6100     {
6101       KillPlayerUnlessExplosionProtected(x, y);
6102       border_explosion = TRUE;
6103     }
6104     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6105     {
6106       Feld[x][y] = Store2[x][y];
6107       Store2[x][y] = 0;
6108       Bang(x, y);
6109       border_explosion = TRUE;
6110     }
6111     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6112     {
6113       AmoebeUmwandeln(x, y);
6114       Store2[x][y] = 0;
6115       border_explosion = TRUE;
6116     }
6117
6118     /* if an element just explodes due to another explosion (chain-reaction),
6119        do not immediately end the new explosion when it was the last frame of
6120        the explosion (as it would be done in the following "if"-statement!) */
6121     if (border_explosion && phase == last_phase)
6122       return;
6123   }
6124
6125   if (phase == last_phase)
6126   {
6127     int element;
6128
6129     element = Feld[x][y] = Store[x][y];
6130     Store[x][y] = Store2[x][y] = 0;
6131     GfxElement[x][y] = EL_UNDEFINED;
6132
6133     /* player can escape from explosions and might therefore be still alive */
6134     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6135         element <= EL_PLAYER_IS_EXPLODING_4)
6136     {
6137       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6138       int explosion_element = EL_PLAYER_1 + player_nr;
6139       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6140       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6141
6142       if (level.use_explosion_element[player_nr])
6143         explosion_element = level.explosion_element[player_nr];
6144
6145       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6146                     element_info[explosion_element].content.e[xx][yy]);
6147     }
6148
6149     /* restore probably existing indestructible background element */
6150     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6151       element = Feld[x][y] = Back[x][y];
6152     Back[x][y] = 0;
6153
6154     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6155     GfxDir[x][y] = MV_NONE;
6156     ChangeDelay[x][y] = 0;
6157     ChangePage[x][y] = -1;
6158
6159 #if USE_NEW_CUSTOM_VALUE
6160     CustomValue[x][y] = 0;
6161 #endif
6162
6163     InitField_WithBug2(x, y, FALSE);
6164
6165     TEST_DrawLevelField(x, y);
6166
6167     TestIfElementTouchesCustomElement(x, y);
6168
6169     if (GFX_CRUMBLED(element))
6170       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6171
6172     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6173       StorePlayer[x][y] = 0;
6174
6175     if (ELEM_IS_PLAYER(element))
6176       RelocatePlayer(x, y, element);
6177   }
6178   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6179   {
6180     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6181     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6182
6183     if (phase == delay)
6184       TEST_DrawLevelFieldCrumbled(x, y);
6185
6186     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6187     {
6188       DrawLevelElement(x, y, Back[x][y]);
6189       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6190     }
6191     else if (IS_WALKABLE_UNDER(Back[x][y]))
6192     {
6193       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6194       DrawLevelElementThruMask(x, y, Back[x][y]);
6195     }
6196     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6197       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6198   }
6199 }
6200
6201 void DynaExplode(int ex, int ey)
6202 {
6203   int i, j;
6204   int dynabomb_element = Feld[ex][ey];
6205   int dynabomb_size = 1;
6206   boolean dynabomb_xl = FALSE;
6207   struct PlayerInfo *player;
6208   static int xy[4][2] =
6209   {
6210     { 0, -1 },
6211     { -1, 0 },
6212     { +1, 0 },
6213     { 0, +1 }
6214   };
6215
6216   if (IS_ACTIVE_BOMB(dynabomb_element))
6217   {
6218     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6219     dynabomb_size = player->dynabomb_size;
6220     dynabomb_xl = player->dynabomb_xl;
6221     player->dynabombs_left++;
6222   }
6223
6224   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6225
6226   for (i = 0; i < NUM_DIRECTIONS; i++)
6227   {
6228     for (j = 1; j <= dynabomb_size; j++)
6229     {
6230       int x = ex + j * xy[i][0];
6231       int y = ey + j * xy[i][1];
6232       int element;
6233
6234       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6235         break;
6236
6237       element = Feld[x][y];
6238
6239       /* do not restart explosions of fields with active bombs */
6240       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6241         continue;
6242
6243       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6244
6245       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6246           !IS_DIGGABLE(element) && !dynabomb_xl)
6247         break;
6248     }
6249   }
6250 }
6251
6252 void Bang(int x, int y)
6253 {
6254   int element = MovingOrBlocked2Element(x, y);
6255   int explosion_type = EX_TYPE_NORMAL;
6256
6257   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6258   {
6259     struct PlayerInfo *player = PLAYERINFO(x, y);
6260
6261 #if USE_FIX_CE_ACTION_WITH_PLAYER
6262     element = Feld[x][y] = player->initial_element;
6263 #else
6264     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6265                             player->element_nr);
6266 #endif
6267
6268     if (level.use_explosion_element[player->index_nr])
6269     {
6270       int explosion_element = level.explosion_element[player->index_nr];
6271
6272       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6273         explosion_type = EX_TYPE_CROSS;
6274       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6275         explosion_type = EX_TYPE_CENTER;
6276     }
6277   }
6278
6279   switch (element)
6280   {
6281     case EL_BUG:
6282     case EL_SPACESHIP:
6283     case EL_BD_BUTTERFLY:
6284     case EL_BD_FIREFLY:
6285     case EL_YAMYAM:
6286     case EL_DARK_YAMYAM:
6287     case EL_ROBOT:
6288     case EL_PACMAN:
6289     case EL_MOLE:
6290       RaiseScoreElement(element);
6291       break;
6292
6293     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6294     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6295     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6296     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6297     case EL_DYNABOMB_INCREASE_NUMBER:
6298     case EL_DYNABOMB_INCREASE_SIZE:
6299     case EL_DYNABOMB_INCREASE_POWER:
6300       explosion_type = EX_TYPE_DYNA;
6301       break;
6302
6303     case EL_DC_LANDMINE:
6304 #if 0
6305     case EL_EM_EXIT_OPEN:
6306     case EL_EM_STEEL_EXIT_OPEN:
6307 #endif
6308       explosion_type = EX_TYPE_CENTER;
6309       break;
6310
6311     case EL_PENGUIN:
6312     case EL_LAMP:
6313     case EL_LAMP_ACTIVE:
6314     case EL_AMOEBA_TO_DIAMOND:
6315       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6316         explosion_type = EX_TYPE_CENTER;
6317       break;
6318
6319     default:
6320       if (element_info[element].explosion_type == EXPLODES_CROSS)
6321         explosion_type = EX_TYPE_CROSS;
6322       else if (element_info[element].explosion_type == EXPLODES_1X1)
6323         explosion_type = EX_TYPE_CENTER;
6324       break;
6325   }
6326
6327   if (explosion_type == EX_TYPE_DYNA)
6328     DynaExplode(x, y);
6329   else
6330     Explode(x, y, EX_PHASE_START, explosion_type);
6331
6332   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6333 }
6334
6335 void SplashAcid(int x, int y)
6336 {
6337   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6338       (!IN_LEV_FIELD(x - 1, y - 2) ||
6339        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6340     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6341
6342   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6343       (!IN_LEV_FIELD(x + 1, y - 2) ||
6344        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6345     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6346
6347   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6348 }
6349
6350 static void InitBeltMovement()
6351 {
6352   static int belt_base_element[4] =
6353   {
6354     EL_CONVEYOR_BELT_1_LEFT,
6355     EL_CONVEYOR_BELT_2_LEFT,
6356     EL_CONVEYOR_BELT_3_LEFT,
6357     EL_CONVEYOR_BELT_4_LEFT
6358   };
6359   static int belt_base_active_element[4] =
6360   {
6361     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6362     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6363     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6364     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6365   };
6366
6367   int x, y, i, j;
6368
6369   /* set frame order for belt animation graphic according to belt direction */
6370   for (i = 0; i < NUM_BELTS; i++)
6371   {
6372     int belt_nr = i;
6373
6374     for (j = 0; j < NUM_BELT_PARTS; j++)
6375     {
6376       int element = belt_base_active_element[belt_nr] + j;
6377       int graphic_1 = el2img(element);
6378       int graphic_2 = el2panelimg(element);
6379
6380       if (game.belt_dir[i] == MV_LEFT)
6381       {
6382         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6383         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6384       }
6385       else
6386       {
6387         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6388         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6389       }
6390     }
6391   }
6392
6393   SCAN_PLAYFIELD(x, y)
6394   {
6395     int element = Feld[x][y];
6396
6397     for (i = 0; i < NUM_BELTS; i++)
6398     {
6399       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6400       {
6401         int e_belt_nr = getBeltNrFromBeltElement(element);
6402         int belt_nr = i;
6403
6404         if (e_belt_nr == belt_nr)
6405         {
6406           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6407
6408           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6409         }
6410       }
6411     }
6412   }
6413 }
6414
6415 static void ToggleBeltSwitch(int x, int y)
6416 {
6417   static int belt_base_element[4] =
6418   {
6419     EL_CONVEYOR_BELT_1_LEFT,
6420     EL_CONVEYOR_BELT_2_LEFT,
6421     EL_CONVEYOR_BELT_3_LEFT,
6422     EL_CONVEYOR_BELT_4_LEFT
6423   };
6424   static int belt_base_active_element[4] =
6425   {
6426     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6427     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6428     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6429     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6430   };
6431   static int belt_base_switch_element[4] =
6432   {
6433     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6434     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6435     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6436     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6437   };
6438   static int belt_move_dir[4] =
6439   {
6440     MV_LEFT,
6441     MV_NONE,
6442     MV_RIGHT,
6443     MV_NONE,
6444   };
6445
6446   int element = Feld[x][y];
6447   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6448   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6449   int belt_dir = belt_move_dir[belt_dir_nr];
6450   int xx, yy, i;
6451
6452   if (!IS_BELT_SWITCH(element))
6453     return;
6454
6455   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6456   game.belt_dir[belt_nr] = belt_dir;
6457
6458   if (belt_dir_nr == 3)
6459     belt_dir_nr = 1;
6460
6461   /* set frame order for belt animation graphic according to belt direction */
6462   for (i = 0; i < NUM_BELT_PARTS; i++)
6463   {
6464     int element = belt_base_active_element[belt_nr] + i;
6465     int graphic_1 = el2img(element);
6466     int graphic_2 = el2panelimg(element);
6467
6468     if (belt_dir == MV_LEFT)
6469     {
6470       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6471       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6472     }
6473     else
6474     {
6475       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6476       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6477     }
6478   }
6479
6480   SCAN_PLAYFIELD(xx, yy)
6481   {
6482     int element = Feld[xx][yy];
6483
6484     if (IS_BELT_SWITCH(element))
6485     {
6486       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6487
6488       if (e_belt_nr == belt_nr)
6489       {
6490         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6491         TEST_DrawLevelField(xx, yy);
6492       }
6493     }
6494     else if (IS_BELT(element) && belt_dir != MV_NONE)
6495     {
6496       int e_belt_nr = getBeltNrFromBeltElement(element);
6497
6498       if (e_belt_nr == belt_nr)
6499       {
6500         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6501
6502         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6503         TEST_DrawLevelField(xx, yy);
6504       }
6505     }
6506     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6507     {
6508       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6509
6510       if (e_belt_nr == belt_nr)
6511       {
6512         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6513
6514         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6515         TEST_DrawLevelField(xx, yy);
6516       }
6517     }
6518   }
6519 }
6520
6521 static void ToggleSwitchgateSwitch(int x, int y)
6522 {
6523   int xx, yy;
6524
6525   game.switchgate_pos = !game.switchgate_pos;
6526
6527   SCAN_PLAYFIELD(xx, yy)
6528   {
6529     int element = Feld[xx][yy];
6530
6531 #if !USE_BOTH_SWITCHGATE_SWITCHES
6532     if (element == EL_SWITCHGATE_SWITCH_UP ||
6533         element == EL_SWITCHGATE_SWITCH_DOWN)
6534     {
6535       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6536       TEST_DrawLevelField(xx, yy);
6537     }
6538     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6539              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6540     {
6541       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6542       TEST_DrawLevelField(xx, yy);
6543     }
6544 #else
6545     if (element == EL_SWITCHGATE_SWITCH_UP)
6546     {
6547       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6548       TEST_DrawLevelField(xx, yy);
6549     }
6550     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6551     {
6552       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6553       TEST_DrawLevelField(xx, yy);
6554     }
6555     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6556     {
6557       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6558       TEST_DrawLevelField(xx, yy);
6559     }
6560     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6561     {
6562       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6563       TEST_DrawLevelField(xx, yy);
6564     }
6565 #endif
6566     else if (element == EL_SWITCHGATE_OPEN ||
6567              element == EL_SWITCHGATE_OPENING)
6568     {
6569       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6570
6571       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6572     }
6573     else if (element == EL_SWITCHGATE_CLOSED ||
6574              element == EL_SWITCHGATE_CLOSING)
6575     {
6576       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6577
6578       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6579     }
6580   }
6581 }
6582
6583 static int getInvisibleActiveFromInvisibleElement(int element)
6584 {
6585   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6586           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6587           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6588           element);
6589 }
6590
6591 static int getInvisibleFromInvisibleActiveElement(int element)
6592 {
6593   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6594           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6595           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6596           element);
6597 }
6598
6599 static void RedrawAllLightSwitchesAndInvisibleElements()
6600 {
6601   int x, y;
6602
6603   SCAN_PLAYFIELD(x, y)
6604   {
6605     int element = Feld[x][y];
6606
6607     if (element == EL_LIGHT_SWITCH &&
6608         game.light_time_left > 0)
6609     {
6610       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6611       TEST_DrawLevelField(x, y);
6612     }
6613     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6614              game.light_time_left == 0)
6615     {
6616       Feld[x][y] = EL_LIGHT_SWITCH;
6617       TEST_DrawLevelField(x, y);
6618     }
6619     else if (element == EL_EMC_DRIPPER &&
6620              game.light_time_left > 0)
6621     {
6622       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6623       TEST_DrawLevelField(x, y);
6624     }
6625     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6626              game.light_time_left == 0)
6627     {
6628       Feld[x][y] = EL_EMC_DRIPPER;
6629       TEST_DrawLevelField(x, y);
6630     }
6631     else if (element == EL_INVISIBLE_STEELWALL ||
6632              element == EL_INVISIBLE_WALL ||
6633              element == EL_INVISIBLE_SAND)
6634     {
6635       if (game.light_time_left > 0)
6636         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6637
6638       TEST_DrawLevelField(x, y);
6639
6640       /* uncrumble neighbour fields, if needed */
6641       if (element == EL_INVISIBLE_SAND)
6642         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6643     }
6644     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6645              element == EL_INVISIBLE_WALL_ACTIVE ||
6646              element == EL_INVISIBLE_SAND_ACTIVE)
6647     {
6648       if (game.light_time_left == 0)
6649         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6650
6651       TEST_DrawLevelField(x, y);
6652
6653       /* re-crumble neighbour fields, if needed */
6654       if (element == EL_INVISIBLE_SAND)
6655         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6656     }
6657   }
6658 }
6659
6660 static void RedrawAllInvisibleElementsForLenses()
6661 {
6662   int x, y;
6663
6664   SCAN_PLAYFIELD(x, y)
6665   {
6666     int element = Feld[x][y];
6667
6668     if (element == EL_EMC_DRIPPER &&
6669         game.lenses_time_left > 0)
6670     {
6671       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6672       TEST_DrawLevelField(x, y);
6673     }
6674     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6675              game.lenses_time_left == 0)
6676     {
6677       Feld[x][y] = EL_EMC_DRIPPER;
6678       TEST_DrawLevelField(x, y);
6679     }
6680     else if (element == EL_INVISIBLE_STEELWALL ||
6681              element == EL_INVISIBLE_WALL ||
6682              element == EL_INVISIBLE_SAND)
6683     {
6684       if (game.lenses_time_left > 0)
6685         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6686
6687       TEST_DrawLevelField(x, y);
6688
6689       /* uncrumble neighbour fields, if needed */
6690       if (element == EL_INVISIBLE_SAND)
6691         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6692     }
6693     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6694              element == EL_INVISIBLE_WALL_ACTIVE ||
6695              element == EL_INVISIBLE_SAND_ACTIVE)
6696     {
6697       if (game.lenses_time_left == 0)
6698         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6699
6700       TEST_DrawLevelField(x, y);
6701
6702       /* re-crumble neighbour fields, if needed */
6703       if (element == EL_INVISIBLE_SAND)
6704         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6705     }
6706   }
6707 }
6708
6709 static void RedrawAllInvisibleElementsForMagnifier()
6710 {
6711   int x, y;
6712
6713   SCAN_PLAYFIELD(x, y)
6714   {
6715     int element = Feld[x][y];
6716
6717     if (element == EL_EMC_FAKE_GRASS &&
6718         game.magnify_time_left > 0)
6719     {
6720       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6721       TEST_DrawLevelField(x, y);
6722     }
6723     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6724              game.magnify_time_left == 0)
6725     {
6726       Feld[x][y] = EL_EMC_FAKE_GRASS;
6727       TEST_DrawLevelField(x, y);
6728     }
6729     else if (IS_GATE_GRAY(element) &&
6730              game.magnify_time_left > 0)
6731     {
6732       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6733                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6734                     IS_EM_GATE_GRAY(element) ?
6735                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6736                     IS_EMC_GATE_GRAY(element) ?
6737                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6738                     IS_DC_GATE_GRAY(element) ?
6739                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6740                     element);
6741       TEST_DrawLevelField(x, y);
6742     }
6743     else if (IS_GATE_GRAY_ACTIVE(element) &&
6744              game.magnify_time_left == 0)
6745     {
6746       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6747                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6748                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6749                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6750                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6751                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6752                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6753                     EL_DC_GATE_WHITE_GRAY :
6754                     element);
6755       TEST_DrawLevelField(x, y);
6756     }
6757   }
6758 }
6759
6760 static void ToggleLightSwitch(int x, int y)
6761 {
6762   int element = Feld[x][y];
6763
6764   game.light_time_left =
6765     (element == EL_LIGHT_SWITCH ?
6766      level.time_light * FRAMES_PER_SECOND : 0);
6767
6768   RedrawAllLightSwitchesAndInvisibleElements();
6769 }
6770
6771 static void ActivateTimegateSwitch(int x, int y)
6772 {
6773   int xx, yy;
6774
6775   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6776
6777   SCAN_PLAYFIELD(xx, yy)
6778   {
6779     int element = Feld[xx][yy];
6780
6781     if (element == EL_TIMEGATE_CLOSED ||
6782         element == EL_TIMEGATE_CLOSING)
6783     {
6784       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6785       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6786     }
6787
6788     /*
6789     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6790     {
6791       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6792       TEST_DrawLevelField(xx, yy);
6793     }
6794     */
6795
6796   }
6797
6798 #if 1
6799   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6800                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6801 #else
6802   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6803 #endif
6804 }
6805
6806 void Impact(int x, int y)
6807 {
6808   boolean last_line = (y == lev_fieldy - 1);
6809   boolean object_hit = FALSE;
6810   boolean impact = (last_line || object_hit);
6811   int element = Feld[x][y];
6812   int smashed = EL_STEELWALL;
6813
6814   if (!last_line)       /* check if element below was hit */
6815   {
6816     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6817       return;
6818
6819     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6820                                          MovDir[x][y + 1] != MV_DOWN ||
6821                                          MovPos[x][y + 1] <= TILEY / 2));
6822
6823     /* do not smash moving elements that left the smashed field in time */
6824     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6825         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6826       object_hit = FALSE;
6827
6828 #if USE_QUICKSAND_IMPACT_BUGFIX
6829     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6830     {
6831       RemoveMovingField(x, y + 1);
6832       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6833       Feld[x][y + 2] = EL_ROCK;
6834       TEST_DrawLevelField(x, y + 2);
6835
6836       object_hit = TRUE;
6837     }
6838
6839     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6840     {
6841       RemoveMovingField(x, y + 1);
6842       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6843       Feld[x][y + 2] = EL_ROCK;
6844       TEST_DrawLevelField(x, y + 2);
6845
6846       object_hit = TRUE;
6847     }
6848 #endif
6849
6850     if (object_hit)
6851       smashed = MovingOrBlocked2Element(x, y + 1);
6852
6853     impact = (last_line || object_hit);
6854   }
6855
6856   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6857   {
6858     SplashAcid(x, y + 1);
6859     return;
6860   }
6861
6862   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6863   /* only reset graphic animation if graphic really changes after impact */
6864   if (impact &&
6865       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6866   {
6867     ResetGfxAnimation(x, y);
6868     TEST_DrawLevelField(x, y);
6869   }
6870
6871   if (impact && CAN_EXPLODE_IMPACT(element))
6872   {
6873     Bang(x, y);
6874     return;
6875   }
6876   else if (impact && element == EL_PEARL &&
6877            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6878   {
6879     ResetGfxAnimation(x, y);
6880
6881     Feld[x][y] = EL_PEARL_BREAKING;
6882     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6883     return;
6884   }
6885   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6886   {
6887     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6888
6889     return;
6890   }
6891
6892   if (impact && element == EL_AMOEBA_DROP)
6893   {
6894     if (object_hit && IS_PLAYER(x, y + 1))
6895       KillPlayerUnlessEnemyProtected(x, y + 1);
6896     else if (object_hit && smashed == EL_PENGUIN)
6897       Bang(x, y + 1);
6898     else
6899     {
6900       Feld[x][y] = EL_AMOEBA_GROWING;
6901       Store[x][y] = EL_AMOEBA_WET;
6902
6903       ResetRandomAnimationValue(x, y);
6904     }
6905     return;
6906   }
6907
6908   if (object_hit)               /* check which object was hit */
6909   {
6910     if ((CAN_PASS_MAGIC_WALL(element) && 
6911          (smashed == EL_MAGIC_WALL ||
6912           smashed == EL_BD_MAGIC_WALL)) ||
6913         (CAN_PASS_DC_MAGIC_WALL(element) &&
6914          smashed == EL_DC_MAGIC_WALL))
6915     {
6916       int xx, yy;
6917       int activated_magic_wall =
6918         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6919          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6920          EL_DC_MAGIC_WALL_ACTIVE);
6921
6922       /* activate magic wall / mill */
6923       SCAN_PLAYFIELD(xx, yy)
6924       {
6925         if (Feld[xx][yy] == smashed)
6926           Feld[xx][yy] = activated_magic_wall;
6927       }
6928
6929       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6930       game.magic_wall_active = TRUE;
6931
6932       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6933                             SND_MAGIC_WALL_ACTIVATING :
6934                             smashed == EL_BD_MAGIC_WALL ?
6935                             SND_BD_MAGIC_WALL_ACTIVATING :
6936                             SND_DC_MAGIC_WALL_ACTIVATING));
6937     }
6938
6939     if (IS_PLAYER(x, y + 1))
6940     {
6941       if (CAN_SMASH_PLAYER(element))
6942       {
6943         KillPlayerUnlessEnemyProtected(x, y + 1);
6944         return;
6945       }
6946     }
6947     else if (smashed == EL_PENGUIN)
6948     {
6949       if (CAN_SMASH_PLAYER(element))
6950       {
6951         Bang(x, y + 1);
6952         return;
6953       }
6954     }
6955     else if (element == EL_BD_DIAMOND)
6956     {
6957       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6958       {
6959         Bang(x, y + 1);
6960         return;
6961       }
6962     }
6963     else if (((element == EL_SP_INFOTRON ||
6964                element == EL_SP_ZONK) &&
6965               (smashed == EL_SP_SNIKSNAK ||
6966                smashed == EL_SP_ELECTRON ||
6967                smashed == EL_SP_DISK_ORANGE)) ||
6968              (element == EL_SP_INFOTRON &&
6969               smashed == EL_SP_DISK_YELLOW))
6970     {
6971       Bang(x, y + 1);
6972       return;
6973     }
6974     else if (CAN_SMASH_EVERYTHING(element))
6975     {
6976       if (IS_CLASSIC_ENEMY(smashed) ||
6977           CAN_EXPLODE_SMASHED(smashed))
6978       {
6979         Bang(x, y + 1);
6980         return;
6981       }
6982       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6983       {
6984         if (smashed == EL_LAMP ||
6985             smashed == EL_LAMP_ACTIVE)
6986         {
6987           Bang(x, y + 1);
6988           return;
6989         }
6990         else if (smashed == EL_NUT)
6991         {
6992           Feld[x][y + 1] = EL_NUT_BREAKING;
6993           PlayLevelSound(x, y, SND_NUT_BREAKING);
6994           RaiseScoreElement(EL_NUT);
6995           return;
6996         }
6997         else if (smashed == EL_PEARL)
6998         {
6999           ResetGfxAnimation(x, y);
7000
7001           Feld[x][y + 1] = EL_PEARL_BREAKING;
7002           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7003           return;
7004         }
7005         else if (smashed == EL_DIAMOND)
7006         {
7007           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7008           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7009           return;
7010         }
7011         else if (IS_BELT_SWITCH(smashed))
7012         {
7013           ToggleBeltSwitch(x, y + 1);
7014         }
7015         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7016                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7017                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7018                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7019         {
7020           ToggleSwitchgateSwitch(x, y + 1);
7021         }
7022         else if (smashed == EL_LIGHT_SWITCH ||
7023                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7024         {
7025           ToggleLightSwitch(x, y + 1);
7026         }
7027         else
7028         {
7029 #if 0
7030           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7031 #endif
7032
7033           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7034
7035           CheckElementChangeBySide(x, y + 1, smashed, element,
7036                                    CE_SWITCHED, CH_SIDE_TOP);
7037           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7038                                             CH_SIDE_TOP);
7039         }
7040       }
7041       else
7042       {
7043         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7044       }
7045     }
7046   }
7047
7048   /* play sound of magic wall / mill */
7049   if (!last_line &&
7050       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7051        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7052        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7053   {
7054     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7055       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7056     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7057       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7058     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7059       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7060
7061     return;
7062   }
7063
7064   /* play sound of object that hits the ground */
7065   if (last_line || object_hit)
7066     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7067 }
7068
7069 inline static void TurnRoundExt(int x, int y)
7070 {
7071   static struct
7072   {
7073     int dx, dy;
7074   } move_xy[] =
7075   {
7076     {  0,  0 },
7077     { -1,  0 },
7078     { +1,  0 },
7079     {  0,  0 },
7080     {  0, -1 },
7081     {  0,  0 }, { 0, 0 }, { 0, 0 },
7082     {  0, +1 }
7083   };
7084   static struct
7085   {
7086     int left, right, back;
7087   } turn[] =
7088   {
7089     { 0,        0,              0        },
7090     { MV_DOWN,  MV_UP,          MV_RIGHT },
7091     { MV_UP,    MV_DOWN,        MV_LEFT  },
7092     { 0,        0,              0        },
7093     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7094     { 0,        0,              0        },
7095     { 0,        0,              0        },
7096     { 0,        0,              0        },
7097     { MV_RIGHT, MV_LEFT,        MV_UP    }
7098   };
7099
7100   int element = Feld[x][y];
7101   int move_pattern = element_info[element].move_pattern;
7102
7103   int old_move_dir = MovDir[x][y];
7104   int left_dir  = turn[old_move_dir].left;
7105   int right_dir = turn[old_move_dir].right;
7106   int back_dir  = turn[old_move_dir].back;
7107
7108   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7109   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7110   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7111   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7112
7113   int left_x  = x + left_dx,  left_y  = y + left_dy;
7114   int right_x = x + right_dx, right_y = y + right_dy;
7115   int move_x  = x + move_dx,  move_y  = y + move_dy;
7116
7117   int xx, yy;
7118
7119   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7120   {
7121     TestIfBadThingTouchesOtherBadThing(x, y);
7122
7123     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7124       MovDir[x][y] = right_dir;
7125     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7126       MovDir[x][y] = left_dir;
7127
7128     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7129       MovDelay[x][y] = 9;
7130     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7131       MovDelay[x][y] = 1;
7132   }
7133   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7134   {
7135     TestIfBadThingTouchesOtherBadThing(x, y);
7136
7137     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7138       MovDir[x][y] = left_dir;
7139     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7140       MovDir[x][y] = right_dir;
7141
7142     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7143       MovDelay[x][y] = 9;
7144     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7145       MovDelay[x][y] = 1;
7146   }
7147   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7148   {
7149     TestIfBadThingTouchesOtherBadThing(x, y);
7150
7151     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7152       MovDir[x][y] = left_dir;
7153     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7154       MovDir[x][y] = right_dir;
7155
7156     if (MovDir[x][y] != old_move_dir)
7157       MovDelay[x][y] = 9;
7158   }
7159   else if (element == EL_YAMYAM)
7160   {
7161     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7162     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7163
7164     if (can_turn_left && can_turn_right)
7165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166     else if (can_turn_left)
7167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168     else if (can_turn_right)
7169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170     else
7171       MovDir[x][y] = back_dir;
7172
7173     MovDelay[x][y] = 16 + 16 * RND(3);
7174   }
7175   else if (element == EL_DARK_YAMYAM)
7176   {
7177     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7178                                                          left_x, left_y);
7179     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7180                                                          right_x, right_y);
7181
7182     if (can_turn_left && can_turn_right)
7183       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7184     else if (can_turn_left)
7185       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7186     else if (can_turn_right)
7187       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7188     else
7189       MovDir[x][y] = back_dir;
7190
7191     MovDelay[x][y] = 16 + 16 * RND(3);
7192   }
7193   else if (element == EL_PACMAN)
7194   {
7195     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7196     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7197
7198     if (can_turn_left && can_turn_right)
7199       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7200     else if (can_turn_left)
7201       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7202     else if (can_turn_right)
7203       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7204     else
7205       MovDir[x][y] = back_dir;
7206
7207     MovDelay[x][y] = 6 + RND(40);
7208   }
7209   else if (element == EL_PIG)
7210   {
7211     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7212     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7213     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7214     boolean should_turn_left, should_turn_right, should_move_on;
7215     int rnd_value = 24;
7216     int rnd = RND(rnd_value);
7217
7218     should_turn_left = (can_turn_left &&
7219                         (!can_move_on ||
7220                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7221                                                    y + back_dy + left_dy)));
7222     should_turn_right = (can_turn_right &&
7223                          (!can_move_on ||
7224                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7225                                                     y + back_dy + right_dy)));
7226     should_move_on = (can_move_on &&
7227                       (!can_turn_left ||
7228                        !can_turn_right ||
7229                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7230                                                  y + move_dy + left_dy) ||
7231                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7232                                                  y + move_dy + right_dy)));
7233
7234     if (should_turn_left || should_turn_right || should_move_on)
7235     {
7236       if (should_turn_left && should_turn_right && should_move_on)
7237         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7238                         rnd < 2 * rnd_value / 3 ? right_dir :
7239                         old_move_dir);
7240       else if (should_turn_left && should_turn_right)
7241         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7242       else if (should_turn_left && should_move_on)
7243         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7244       else if (should_turn_right && should_move_on)
7245         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7246       else if (should_turn_left)
7247         MovDir[x][y] = left_dir;
7248       else if (should_turn_right)
7249         MovDir[x][y] = right_dir;
7250       else if (should_move_on)
7251         MovDir[x][y] = old_move_dir;
7252     }
7253     else if (can_move_on && rnd > rnd_value / 8)
7254       MovDir[x][y] = old_move_dir;
7255     else if (can_turn_left && can_turn_right)
7256       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7257     else if (can_turn_left && rnd > rnd_value / 8)
7258       MovDir[x][y] = left_dir;
7259     else if (can_turn_right && rnd > rnd_value/8)
7260       MovDir[x][y] = right_dir;
7261     else
7262       MovDir[x][y] = back_dir;
7263
7264     xx = x + move_xy[MovDir[x][y]].dx;
7265     yy = y + move_xy[MovDir[x][y]].dy;
7266
7267     if (!IN_LEV_FIELD(xx, yy) ||
7268         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7269       MovDir[x][y] = old_move_dir;
7270
7271     MovDelay[x][y] = 0;
7272   }
7273   else if (element == EL_DRAGON)
7274   {
7275     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7276     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7277     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7278     int rnd_value = 24;
7279     int rnd = RND(rnd_value);
7280
7281     if (can_move_on && rnd > rnd_value / 8)
7282       MovDir[x][y] = old_move_dir;
7283     else if (can_turn_left && can_turn_right)
7284       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7285     else if (can_turn_left && rnd > rnd_value / 8)
7286       MovDir[x][y] = left_dir;
7287     else if (can_turn_right && rnd > rnd_value / 8)
7288       MovDir[x][y] = right_dir;
7289     else
7290       MovDir[x][y] = back_dir;
7291
7292     xx = x + move_xy[MovDir[x][y]].dx;
7293     yy = y + move_xy[MovDir[x][y]].dy;
7294
7295     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7296       MovDir[x][y] = old_move_dir;
7297
7298     MovDelay[x][y] = 0;
7299   }
7300   else if (element == EL_MOLE)
7301   {
7302     boolean can_move_on =
7303       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7304                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7305                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7306     if (!can_move_on)
7307     {
7308       boolean can_turn_left =
7309         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7310                               IS_AMOEBOID(Feld[left_x][left_y])));
7311
7312       boolean can_turn_right =
7313         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7314                               IS_AMOEBOID(Feld[right_x][right_y])));
7315
7316       if (can_turn_left && can_turn_right)
7317         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7318       else if (can_turn_left)
7319         MovDir[x][y] = left_dir;
7320       else
7321         MovDir[x][y] = right_dir;
7322     }
7323
7324     if (MovDir[x][y] != old_move_dir)
7325       MovDelay[x][y] = 9;
7326   }
7327   else if (element == EL_BALLOON)
7328   {
7329     MovDir[x][y] = game.wind_direction;
7330     MovDelay[x][y] = 0;
7331   }
7332   else if (element == EL_SPRING)
7333   {
7334 #if USE_NEW_SPRING_BUMPER
7335     if (MovDir[x][y] & MV_HORIZONTAL)
7336     {
7337       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7338           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7339       {
7340         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7341         ResetGfxAnimation(move_x, move_y);
7342         TEST_DrawLevelField(move_x, move_y);
7343
7344         MovDir[x][y] = back_dir;
7345       }
7346       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7347                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7348         MovDir[x][y] = MV_NONE;
7349     }
7350 #else
7351     if (MovDir[x][y] & MV_HORIZONTAL &&
7352         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7353          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7354       MovDir[x][y] = MV_NONE;
7355 #endif
7356
7357     MovDelay[x][y] = 0;
7358   }
7359   else if (element == EL_ROBOT ||
7360            element == EL_SATELLITE ||
7361            element == EL_PENGUIN ||
7362            element == EL_EMC_ANDROID)
7363   {
7364     int attr_x = -1, attr_y = -1;
7365
7366     if (AllPlayersGone)
7367     {
7368       attr_x = ExitX;
7369       attr_y = ExitY;
7370     }
7371     else
7372     {
7373       int i;
7374
7375       for (i = 0; i < MAX_PLAYERS; i++)
7376       {
7377         struct PlayerInfo *player = &stored_player[i];
7378         int jx = player->jx, jy = player->jy;
7379
7380         if (!player->active)
7381           continue;
7382
7383         if (attr_x == -1 ||
7384             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7385         {
7386           attr_x = jx;
7387           attr_y = jy;
7388         }
7389       }
7390     }
7391
7392     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7393         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7394          game.engine_version < VERSION_IDENT(3,1,0,0)))
7395     {
7396       attr_x = ZX;
7397       attr_y = ZY;
7398     }
7399
7400     if (element == EL_PENGUIN)
7401     {
7402       int i;
7403       static int xy[4][2] =
7404       {
7405         { 0, -1 },
7406         { -1, 0 },
7407         { +1, 0 },
7408         { 0, +1 }
7409       };
7410
7411       for (i = 0; i < NUM_DIRECTIONS; i++)
7412       {
7413         int ex = x + xy[i][0];
7414         int ey = y + xy[i][1];
7415
7416         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7417                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7418                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7419                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7420         {
7421           attr_x = ex;
7422           attr_y = ey;
7423           break;
7424         }
7425       }
7426     }
7427
7428     MovDir[x][y] = MV_NONE;
7429     if (attr_x < x)
7430       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7431     else if (attr_x > x)
7432       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7433     if (attr_y < y)
7434       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7435     else if (attr_y > y)
7436       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7437
7438     if (element == EL_ROBOT)
7439     {
7440       int newx, newy;
7441
7442       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7444       Moving2Blocked(x, y, &newx, &newy);
7445
7446       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7447         MovDelay[x][y] = 8 + 8 * !RND(3);
7448       else
7449         MovDelay[x][y] = 16;
7450     }
7451     else if (element == EL_PENGUIN)
7452     {
7453       int newx, newy;
7454
7455       MovDelay[x][y] = 1;
7456
7457       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7458       {
7459         boolean first_horiz = RND(2);
7460         int new_move_dir = MovDir[x][y];
7461
7462         MovDir[x][y] =
7463           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7464         Moving2Blocked(x, y, &newx, &newy);
7465
7466         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7467           return;
7468
7469         MovDir[x][y] =
7470           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471         Moving2Blocked(x, y, &newx, &newy);
7472
7473         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7474           return;
7475
7476         MovDir[x][y] = old_move_dir;
7477         return;
7478       }
7479     }
7480     else if (element == EL_SATELLITE)
7481     {
7482       int newx, newy;
7483
7484       MovDelay[x][y] = 1;
7485
7486       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7487       {
7488         boolean first_horiz = RND(2);
7489         int new_move_dir = MovDir[x][y];
7490
7491         MovDir[x][y] =
7492           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493         Moving2Blocked(x, y, &newx, &newy);
7494
7495         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7496           return;
7497
7498         MovDir[x][y] =
7499           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500         Moving2Blocked(x, y, &newx, &newy);
7501
7502         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7503           return;
7504
7505         MovDir[x][y] = old_move_dir;
7506         return;
7507       }
7508     }
7509     else if (element == EL_EMC_ANDROID)
7510     {
7511       static int check_pos[16] =
7512       {
7513         -1,             /*  0 => (invalid)          */
7514         7,              /*  1 => MV_LEFT            */
7515         3,              /*  2 => MV_RIGHT           */
7516         -1,             /*  3 => (invalid)          */
7517         1,              /*  4 =>            MV_UP   */
7518         0,              /*  5 => MV_LEFT  | MV_UP   */
7519         2,              /*  6 => MV_RIGHT | MV_UP   */
7520         -1,             /*  7 => (invalid)          */
7521         5,              /*  8 =>            MV_DOWN */
7522         6,              /*  9 => MV_LEFT  | MV_DOWN */
7523         4,              /* 10 => MV_RIGHT | MV_DOWN */
7524         -1,             /* 11 => (invalid)          */
7525         -1,             /* 12 => (invalid)          */
7526         -1,             /* 13 => (invalid)          */
7527         -1,             /* 14 => (invalid)          */
7528         -1,             /* 15 => (invalid)          */
7529       };
7530       static struct
7531       {
7532         int dx, dy;
7533         int dir;
7534       } check_xy[8] =
7535       {
7536         { -1, -1,       MV_LEFT  | MV_UP   },
7537         {  0, -1,                  MV_UP   },
7538         { +1, -1,       MV_RIGHT | MV_UP   },
7539         { +1,  0,       MV_RIGHT           },
7540         { +1, +1,       MV_RIGHT | MV_DOWN },
7541         {  0, +1,                  MV_DOWN },
7542         { -1, +1,       MV_LEFT  | MV_DOWN },
7543         { -1,  0,       MV_LEFT            },
7544       };
7545       int start_pos, check_order;
7546       boolean can_clone = FALSE;
7547       int i;
7548
7549       /* check if there is any free field around current position */
7550       for (i = 0; i < 8; i++)
7551       {
7552         int newx = x + check_xy[i].dx;
7553         int newy = y + check_xy[i].dy;
7554
7555         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7556         {
7557           can_clone = TRUE;
7558
7559           break;
7560         }
7561       }
7562
7563       if (can_clone)            /* randomly find an element to clone */
7564       {
7565         can_clone = FALSE;
7566
7567         start_pos = check_pos[RND(8)];
7568         check_order = (RND(2) ? -1 : +1);
7569
7570         for (i = 0; i < 8; i++)
7571         {
7572           int pos_raw = start_pos + i * check_order;
7573           int pos = (pos_raw + 8) % 8;
7574           int newx = x + check_xy[pos].dx;
7575           int newy = y + check_xy[pos].dy;
7576
7577           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7578           {
7579             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7580             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7581
7582             Store[x][y] = Feld[newx][newy];
7583
7584             can_clone = TRUE;
7585
7586             break;
7587           }
7588         }
7589       }
7590
7591       if (can_clone)            /* randomly find a direction to move */
7592       {
7593         can_clone = FALSE;
7594
7595         start_pos = check_pos[RND(8)];
7596         check_order = (RND(2) ? -1 : +1);
7597
7598         for (i = 0; i < 8; i++)
7599         {
7600           int pos_raw = start_pos + i * check_order;
7601           int pos = (pos_raw + 8) % 8;
7602           int newx = x + check_xy[pos].dx;
7603           int newy = y + check_xy[pos].dy;
7604           int new_move_dir = check_xy[pos].dir;
7605
7606           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7607           {
7608             MovDir[x][y] = new_move_dir;
7609             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7610
7611             can_clone = TRUE;
7612
7613             break;
7614           }
7615         }
7616       }
7617
7618       if (can_clone)            /* cloning and moving successful */
7619         return;
7620
7621       /* cannot clone -- try to move towards player */
7622
7623       start_pos = check_pos[MovDir[x][y] & 0x0f];
7624       check_order = (RND(2) ? -1 : +1);
7625
7626       for (i = 0; i < 3; i++)
7627       {
7628         /* first check start_pos, then previous/next or (next/previous) pos */
7629         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7630         int pos = (pos_raw + 8) % 8;
7631         int newx = x + check_xy[pos].dx;
7632         int newy = y + check_xy[pos].dy;
7633         int new_move_dir = check_xy[pos].dir;
7634
7635         if (IS_PLAYER(newx, newy))
7636           break;
7637
7638         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7639         {
7640           MovDir[x][y] = new_move_dir;
7641           MovDelay[x][y] = level.android_move_time * 8 + 1;
7642
7643           break;
7644         }
7645       }
7646     }
7647   }
7648   else if (move_pattern == MV_TURNING_LEFT ||
7649            move_pattern == MV_TURNING_RIGHT ||
7650            move_pattern == MV_TURNING_LEFT_RIGHT ||
7651            move_pattern == MV_TURNING_RIGHT_LEFT ||
7652            move_pattern == MV_TURNING_RANDOM ||
7653            move_pattern == MV_ALL_DIRECTIONS)
7654   {
7655     boolean can_turn_left =
7656       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7657     boolean can_turn_right =
7658       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7659
7660     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7661       return;
7662
7663     if (move_pattern == MV_TURNING_LEFT)
7664       MovDir[x][y] = left_dir;
7665     else if (move_pattern == MV_TURNING_RIGHT)
7666       MovDir[x][y] = right_dir;
7667     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7668       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7669     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7670       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7671     else if (move_pattern == MV_TURNING_RANDOM)
7672       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7673                       can_turn_right && !can_turn_left ? right_dir :
7674                       RND(2) ? left_dir : right_dir);
7675     else if (can_turn_left && can_turn_right)
7676       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7677     else if (can_turn_left)
7678       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7679     else if (can_turn_right)
7680       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7681     else
7682       MovDir[x][y] = back_dir;
7683
7684     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7685   }
7686   else if (move_pattern == MV_HORIZONTAL ||
7687            move_pattern == MV_VERTICAL)
7688   {
7689     if (move_pattern & old_move_dir)
7690       MovDir[x][y] = back_dir;
7691     else if (move_pattern == MV_HORIZONTAL)
7692       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7693     else if (move_pattern == MV_VERTICAL)
7694       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7695
7696     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7697   }
7698   else if (move_pattern & MV_ANY_DIRECTION)
7699   {
7700     MovDir[x][y] = move_pattern;
7701     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7702   }
7703   else if (move_pattern & MV_WIND_DIRECTION)
7704   {
7705     MovDir[x][y] = game.wind_direction;
7706     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7707   }
7708   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7709   {
7710     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7711       MovDir[x][y] = left_dir;
7712     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7713       MovDir[x][y] = right_dir;
7714
7715     if (MovDir[x][y] != old_move_dir)
7716       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7717   }
7718   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7719   {
7720     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7721       MovDir[x][y] = right_dir;
7722     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7723       MovDir[x][y] = left_dir;
7724
7725     if (MovDir[x][y] != old_move_dir)
7726       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7727   }
7728   else if (move_pattern == MV_TOWARDS_PLAYER ||
7729            move_pattern == MV_AWAY_FROM_PLAYER)
7730   {
7731     int attr_x = -1, attr_y = -1;
7732     int newx, newy;
7733     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7734
7735     if (AllPlayersGone)
7736     {
7737       attr_x = ExitX;
7738       attr_y = ExitY;
7739     }
7740     else
7741     {
7742       int i;
7743
7744       for (i = 0; i < MAX_PLAYERS; i++)
7745       {
7746         struct PlayerInfo *player = &stored_player[i];
7747         int jx = player->jx, jy = player->jy;
7748
7749         if (!player->active)
7750           continue;
7751
7752         if (attr_x == -1 ||
7753             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7754         {
7755           attr_x = jx;
7756           attr_y = jy;
7757         }
7758       }
7759     }
7760
7761     MovDir[x][y] = MV_NONE;
7762     if (attr_x < x)
7763       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7764     else if (attr_x > x)
7765       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7766     if (attr_y < y)
7767       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7768     else if (attr_y > y)
7769       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7770
7771     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7772
7773     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7774     {
7775       boolean first_horiz = RND(2);
7776       int new_move_dir = MovDir[x][y];
7777
7778       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7779       {
7780         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7781         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7782
7783         return;
7784       }
7785
7786       MovDir[x][y] =
7787         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7788       Moving2Blocked(x, y, &newx, &newy);
7789
7790       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7791         return;
7792
7793       MovDir[x][y] =
7794         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7795       Moving2Blocked(x, y, &newx, &newy);
7796
7797       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7798         return;
7799
7800       MovDir[x][y] = old_move_dir;
7801     }
7802   }
7803   else if (move_pattern == MV_WHEN_PUSHED ||
7804            move_pattern == MV_WHEN_DROPPED)
7805   {
7806     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7807       MovDir[x][y] = MV_NONE;
7808
7809     MovDelay[x][y] = 0;
7810   }
7811   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7812   {
7813     static int test_xy[7][2] =
7814     {
7815       { 0, -1 },
7816       { -1, 0 },
7817       { +1, 0 },
7818       { 0, +1 },
7819       { 0, -1 },
7820       { -1, 0 },
7821       { +1, 0 },
7822     };
7823     static int test_dir[7] =
7824     {
7825       MV_UP,
7826       MV_LEFT,
7827       MV_RIGHT,
7828       MV_DOWN,
7829       MV_UP,
7830       MV_LEFT,
7831       MV_RIGHT,
7832     };
7833     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7834     int move_preference = -1000000;     /* start with very low preference */
7835     int new_move_dir = MV_NONE;
7836     int start_test = RND(4);
7837     int i;
7838
7839     for (i = 0; i < NUM_DIRECTIONS; i++)
7840     {
7841       int move_dir = test_dir[start_test + i];
7842       int move_dir_preference;
7843
7844       xx = x + test_xy[start_test + i][0];
7845       yy = y + test_xy[start_test + i][1];
7846
7847       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7848           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7849       {
7850         new_move_dir = move_dir;
7851
7852         break;
7853       }
7854
7855       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7856         continue;
7857
7858       move_dir_preference = -1 * RunnerVisit[xx][yy];
7859       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7860         move_dir_preference = PlayerVisit[xx][yy];
7861
7862       if (move_dir_preference > move_preference)
7863       {
7864         /* prefer field that has not been visited for the longest time */
7865         move_preference = move_dir_preference;
7866         new_move_dir = move_dir;
7867       }
7868       else if (move_dir_preference == move_preference &&
7869                move_dir == old_move_dir)
7870       {
7871         /* prefer last direction when all directions are preferred equally */
7872         move_preference = move_dir_preference;
7873         new_move_dir = move_dir;
7874       }
7875     }
7876
7877     MovDir[x][y] = new_move_dir;
7878     if (old_move_dir != new_move_dir)
7879       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7880   }
7881 }
7882
7883 static void TurnRound(int x, int y)
7884 {
7885   int direction = MovDir[x][y];
7886
7887   TurnRoundExt(x, y);
7888
7889   GfxDir[x][y] = MovDir[x][y];
7890
7891   if (direction != MovDir[x][y])
7892     GfxFrame[x][y] = 0;
7893
7894   if (MovDelay[x][y])
7895     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7896
7897   ResetGfxFrame(x, y, FALSE);
7898 }
7899
7900 static boolean JustBeingPushed(int x, int y)
7901 {
7902   int i;
7903
7904   for (i = 0; i < MAX_PLAYERS; i++)
7905   {
7906     struct PlayerInfo *player = &stored_player[i];
7907
7908     if (player->active && player->is_pushing && player->MovPos)
7909     {
7910       int next_jx = player->jx + (player->jx - player->last_jx);
7911       int next_jy = player->jy + (player->jy - player->last_jy);
7912
7913       if (x == next_jx && y == next_jy)
7914         return TRUE;
7915     }
7916   }
7917
7918   return FALSE;
7919 }
7920
7921 void StartMoving(int x, int y)
7922 {
7923   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7924   int element = Feld[x][y];
7925
7926   if (Stop[x][y])
7927     return;
7928
7929   if (MovDelay[x][y] == 0)
7930     GfxAction[x][y] = ACTION_DEFAULT;
7931
7932   if (CAN_FALL(element) && y < lev_fieldy - 1)
7933   {
7934     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7935         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7936       if (JustBeingPushed(x, y))
7937         return;
7938
7939     if (element == EL_QUICKSAND_FULL)
7940     {
7941       if (IS_FREE(x, y + 1))
7942       {
7943         InitMovingField(x, y, MV_DOWN);
7944         started_moving = TRUE;
7945
7946         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7947 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7948         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7949           Store[x][y] = EL_ROCK;
7950 #else
7951         Store[x][y] = EL_ROCK;
7952 #endif
7953
7954         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7955       }
7956       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7957       {
7958         if (!MovDelay[x][y])
7959         {
7960           MovDelay[x][y] = TILEY + 1;
7961
7962           ResetGfxAnimation(x, y);
7963           ResetGfxAnimation(x, y + 1);
7964         }
7965
7966         if (MovDelay[x][y])
7967         {
7968           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7969           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7970
7971           MovDelay[x][y]--;
7972           if (MovDelay[x][y])
7973             return;
7974         }
7975
7976         Feld[x][y] = EL_QUICKSAND_EMPTY;
7977         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7978         Store[x][y + 1] = Store[x][y];
7979         Store[x][y] = 0;
7980
7981         PlayLevelSoundAction(x, y, ACTION_FILLING);
7982       }
7983       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7984       {
7985         if (!MovDelay[x][y])
7986         {
7987           MovDelay[x][y] = TILEY + 1;
7988
7989           ResetGfxAnimation(x, y);
7990           ResetGfxAnimation(x, y + 1);
7991         }
7992
7993         if (MovDelay[x][y])
7994         {
7995           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7996           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7997
7998           MovDelay[x][y]--;
7999           if (MovDelay[x][y])
8000             return;
8001         }
8002
8003         Feld[x][y] = EL_QUICKSAND_EMPTY;
8004         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8005         Store[x][y + 1] = Store[x][y];
8006         Store[x][y] = 0;
8007
8008         PlayLevelSoundAction(x, y, ACTION_FILLING);
8009       }
8010     }
8011     else if (element == EL_QUICKSAND_FAST_FULL)
8012     {
8013       if (IS_FREE(x, y + 1))
8014       {
8015         InitMovingField(x, y, MV_DOWN);
8016         started_moving = TRUE;
8017
8018         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8019 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8020         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8021           Store[x][y] = EL_ROCK;
8022 #else
8023         Store[x][y] = EL_ROCK;
8024 #endif
8025
8026         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8027       }
8028       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8029       {
8030         if (!MovDelay[x][y])
8031         {
8032           MovDelay[x][y] = TILEY + 1;
8033
8034           ResetGfxAnimation(x, y);
8035           ResetGfxAnimation(x, y + 1);
8036         }
8037
8038         if (MovDelay[x][y])
8039         {
8040           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8041           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8042
8043           MovDelay[x][y]--;
8044           if (MovDelay[x][y])
8045             return;
8046         }
8047
8048         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8049         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8050         Store[x][y + 1] = Store[x][y];
8051         Store[x][y] = 0;
8052
8053         PlayLevelSoundAction(x, y, ACTION_FILLING);
8054       }
8055       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8056       {
8057         if (!MovDelay[x][y])
8058         {
8059           MovDelay[x][y] = TILEY + 1;
8060
8061           ResetGfxAnimation(x, y);
8062           ResetGfxAnimation(x, y + 1);
8063         }
8064
8065         if (MovDelay[x][y])
8066         {
8067           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8068           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8069
8070           MovDelay[x][y]--;
8071           if (MovDelay[x][y])
8072             return;
8073         }
8074
8075         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8076         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8077         Store[x][y + 1] = Store[x][y];
8078         Store[x][y] = 0;
8079
8080         PlayLevelSoundAction(x, y, ACTION_FILLING);
8081       }
8082     }
8083     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8085     {
8086       InitMovingField(x, y, MV_DOWN);
8087       started_moving = TRUE;
8088
8089       Feld[x][y] = EL_QUICKSAND_FILLING;
8090       Store[x][y] = element;
8091
8092       PlayLevelSoundAction(x, y, ACTION_FILLING);
8093     }
8094     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8095              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8096     {
8097       InitMovingField(x, y, MV_DOWN);
8098       started_moving = TRUE;
8099
8100       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8101       Store[x][y] = element;
8102
8103       PlayLevelSoundAction(x, y, ACTION_FILLING);
8104     }
8105     else if (element == EL_MAGIC_WALL_FULL)
8106     {
8107       if (IS_FREE(x, y + 1))
8108       {
8109         InitMovingField(x, y, MV_DOWN);
8110         started_moving = TRUE;
8111
8112         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8113         Store[x][y] = EL_CHANGED(Store[x][y]);
8114       }
8115       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8116       {
8117         if (!MovDelay[x][y])
8118           MovDelay[x][y] = TILEY/4 + 1;
8119
8120         if (MovDelay[x][y])
8121         {
8122           MovDelay[x][y]--;
8123           if (MovDelay[x][y])
8124             return;
8125         }
8126
8127         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8128         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8129         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8130         Store[x][y] = 0;
8131       }
8132     }
8133     else if (element == EL_BD_MAGIC_WALL_FULL)
8134     {
8135       if (IS_FREE(x, y + 1))
8136       {
8137         InitMovingField(x, y, MV_DOWN);
8138         started_moving = TRUE;
8139
8140         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8141         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8142       }
8143       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8144       {
8145         if (!MovDelay[x][y])
8146           MovDelay[x][y] = TILEY/4 + 1;
8147
8148         if (MovDelay[x][y])
8149         {
8150           MovDelay[x][y]--;
8151           if (MovDelay[x][y])
8152             return;
8153         }
8154
8155         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8156         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8157         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8158         Store[x][y] = 0;
8159       }
8160     }
8161     else if (element == EL_DC_MAGIC_WALL_FULL)
8162     {
8163       if (IS_FREE(x, y + 1))
8164       {
8165         InitMovingField(x, y, MV_DOWN);
8166         started_moving = TRUE;
8167
8168         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8169         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8170       }
8171       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8172       {
8173         if (!MovDelay[x][y])
8174           MovDelay[x][y] = TILEY/4 + 1;
8175
8176         if (MovDelay[x][y])
8177         {
8178           MovDelay[x][y]--;
8179           if (MovDelay[x][y])
8180             return;
8181         }
8182
8183         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8184         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8185         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8186         Store[x][y] = 0;
8187       }
8188     }
8189     else if ((CAN_PASS_MAGIC_WALL(element) &&
8190               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8191                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8192              (CAN_PASS_DC_MAGIC_WALL(element) &&
8193               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8194
8195     {
8196       InitMovingField(x, y, MV_DOWN);
8197       started_moving = TRUE;
8198
8199       Feld[x][y] =
8200         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8201          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8202          EL_DC_MAGIC_WALL_FILLING);
8203       Store[x][y] = element;
8204     }
8205     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8206     {
8207       SplashAcid(x, y + 1);
8208
8209       InitMovingField(x, y, MV_DOWN);
8210       started_moving = TRUE;
8211
8212       Store[x][y] = EL_ACID;
8213     }
8214     else if (
8215 #if USE_FIX_IMPACT_COLLISION
8216              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8218 #else
8219              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8220               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8221 #endif
8222              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8223               CAN_FALL(element) && WasJustFalling[x][y] &&
8224               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8225
8226              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8227               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8228               (Feld[x][y + 1] == EL_BLOCKED)))
8229     {
8230       /* this is needed for a special case not covered by calling "Impact()"
8231          from "ContinueMoving()": if an element moves to a tile directly below
8232          another element which was just falling on that tile (which was empty
8233          in the previous frame), the falling element above would just stop
8234          instead of smashing the element below (in previous version, the above
8235          element was just checked for "moving" instead of "falling", resulting
8236          in incorrect smashes caused by horizontal movement of the above
8237          element; also, the case of the player being the element to smash was
8238          simply not covered here... :-/ ) */
8239
8240       CheckCollision[x][y] = 0;
8241       CheckImpact[x][y] = 0;
8242
8243       Impact(x, y);
8244     }
8245     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8246     {
8247       if (MovDir[x][y] == MV_NONE)
8248       {
8249         InitMovingField(x, y, MV_DOWN);
8250         started_moving = TRUE;
8251       }
8252     }
8253     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8254     {
8255       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8256         MovDir[x][y] = MV_DOWN;
8257
8258       InitMovingField(x, y, MV_DOWN);
8259       started_moving = TRUE;
8260     }
8261     else if (element == EL_AMOEBA_DROP)
8262     {
8263       Feld[x][y] = EL_AMOEBA_GROWING;
8264       Store[x][y] = EL_AMOEBA_WET;
8265     }
8266     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8267               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8268              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8269              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8270     {
8271       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8272                                 (IS_FREE(x - 1, y + 1) ||
8273                                  Feld[x - 1][y + 1] == EL_ACID));
8274       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8275                                 (IS_FREE(x + 1, y + 1) ||
8276                                  Feld[x + 1][y + 1] == EL_ACID));
8277       boolean can_fall_any  = (can_fall_left || can_fall_right);
8278       boolean can_fall_both = (can_fall_left && can_fall_right);
8279       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8280
8281 #if USE_NEW_ALL_SLIPPERY
8282       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8283       {
8284         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8285           can_fall_right = FALSE;
8286         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8287           can_fall_left = FALSE;
8288         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8289           can_fall_right = FALSE;
8290         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291           can_fall_left = FALSE;
8292
8293         can_fall_any  = (can_fall_left || can_fall_right);
8294         can_fall_both = FALSE;
8295       }
8296 #else
8297       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8298       {
8299         if (slippery_type == SLIPPERY_ONLY_LEFT)
8300           can_fall_right = FALSE;
8301         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8302           can_fall_left = FALSE;
8303         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8304           can_fall_right = FALSE;
8305         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8306           can_fall_left = FALSE;
8307
8308         can_fall_any  = (can_fall_left || can_fall_right);
8309         can_fall_both = (can_fall_left && can_fall_right);
8310       }
8311 #endif
8312
8313 #if USE_NEW_ALL_SLIPPERY
8314 #else
8315 #if USE_NEW_SP_SLIPPERY
8316       /* !!! better use the same properties as for custom elements here !!! */
8317       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8318                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8319       {
8320         can_fall_right = FALSE;         /* slip down on left side */
8321         can_fall_both = FALSE;
8322       }
8323 #endif
8324 #endif
8325
8326 #if USE_NEW_ALL_SLIPPERY
8327       if (can_fall_both)
8328       {
8329         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330           can_fall_right = FALSE;       /* slip down on left side */
8331         else
8332           can_fall_left = !(can_fall_right = RND(2));
8333
8334         can_fall_both = FALSE;
8335       }
8336 #else
8337       if (can_fall_both)
8338       {
8339         if (game.emulation == EMU_BOULDERDASH ||
8340             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8341           can_fall_right = FALSE;       /* slip down on left side */
8342         else
8343           can_fall_left = !(can_fall_right = RND(2));
8344
8345         can_fall_both = FALSE;
8346       }
8347 #endif
8348
8349       if (can_fall_any)
8350       {
8351         /* if not determined otherwise, prefer left side for slipping down */
8352         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8353         started_moving = TRUE;
8354       }
8355     }
8356 #if 0
8357     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8358 #else
8359     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8360 #endif
8361     {
8362       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8363       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8364       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8365       int belt_dir = game.belt_dir[belt_nr];
8366
8367       if ((belt_dir == MV_LEFT  && left_is_free) ||
8368           (belt_dir == MV_RIGHT && right_is_free))
8369       {
8370         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8371
8372         InitMovingField(x, y, belt_dir);
8373         started_moving = TRUE;
8374
8375         Pushed[x][y] = TRUE;
8376         Pushed[nextx][y] = TRUE;
8377
8378         GfxAction[x][y] = ACTION_DEFAULT;
8379       }
8380       else
8381       {
8382         MovDir[x][y] = 0;       /* if element was moving, stop it */
8383       }
8384     }
8385   }
8386
8387   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8388 #if 0
8389   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8390 #else
8391   if (CAN_MOVE(element) && !started_moving)
8392 #endif
8393   {
8394     int move_pattern = element_info[element].move_pattern;
8395     int newx, newy;
8396
8397 #if 0
8398 #if DEBUG
8399     if (MovDir[x][y] == MV_NONE)
8400     {
8401       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8402              x, y, element, element_info[element].token_name);
8403       printf("StartMoving(): This should never happen!\n");
8404     }
8405 #endif
8406 #endif
8407
8408     Moving2Blocked(x, y, &newx, &newy);
8409
8410     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8411       return;
8412
8413     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8414         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8415     {
8416       WasJustMoving[x][y] = 0;
8417       CheckCollision[x][y] = 0;
8418
8419       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8420
8421       if (Feld[x][y] != element)        /* element has changed */
8422         return;
8423     }
8424
8425     if (!MovDelay[x][y])        /* start new movement phase */
8426     {
8427       /* all objects that can change their move direction after each step
8428          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8429
8430       if (element != EL_YAMYAM &&
8431           element != EL_DARK_YAMYAM &&
8432           element != EL_PACMAN &&
8433           !(move_pattern & MV_ANY_DIRECTION) &&
8434           move_pattern != MV_TURNING_LEFT &&
8435           move_pattern != MV_TURNING_RIGHT &&
8436           move_pattern != MV_TURNING_LEFT_RIGHT &&
8437           move_pattern != MV_TURNING_RIGHT_LEFT &&
8438           move_pattern != MV_TURNING_RANDOM)
8439       {
8440         TurnRound(x, y);
8441
8442         if (MovDelay[x][y] && (element == EL_BUG ||
8443                                element == EL_SPACESHIP ||
8444                                element == EL_SP_SNIKSNAK ||
8445                                element == EL_SP_ELECTRON ||
8446                                element == EL_MOLE))
8447           TEST_DrawLevelField(x, y);
8448       }
8449     }
8450
8451     if (MovDelay[x][y])         /* wait some time before next movement */
8452     {
8453       MovDelay[x][y]--;
8454
8455       if (element == EL_ROBOT ||
8456           element == EL_YAMYAM ||
8457           element == EL_DARK_YAMYAM)
8458       {
8459         DrawLevelElementAnimationIfNeeded(x, y, element);
8460         PlayLevelSoundAction(x, y, ACTION_WAITING);
8461       }
8462       else if (element == EL_SP_ELECTRON)
8463         DrawLevelElementAnimationIfNeeded(x, y, element);
8464       else if (element == EL_DRAGON)
8465       {
8466         int i;
8467         int dir = MovDir[x][y];
8468         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8469         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8470         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8471                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8472                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8473                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8474         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8475
8476         GfxAction[x][y] = ACTION_ATTACKING;
8477
8478         if (IS_PLAYER(x, y))
8479           DrawPlayerField(x, y);
8480         else
8481           TEST_DrawLevelField(x, y);
8482
8483         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8484
8485         for (i = 1; i <= 3; i++)
8486         {
8487           int xx = x + i * dx;
8488           int yy = y + i * dy;
8489           int sx = SCREENX(xx);
8490           int sy = SCREENY(yy);
8491           int flame_graphic = graphic + (i - 1);
8492
8493           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8494             break;
8495
8496           if (MovDelay[x][y])
8497           {
8498             int flamed = MovingOrBlocked2Element(xx, yy);
8499
8500             /* !!! */
8501 #if 0
8502             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8503               Bang(xx, yy);
8504             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8505               RemoveMovingField(xx, yy);
8506             else
8507               RemoveField(xx, yy);
8508 #else
8509             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8510               Bang(xx, yy);
8511             else
8512               RemoveMovingField(xx, yy);
8513 #endif
8514
8515             ChangeDelay[xx][yy] = 0;
8516
8517             Feld[xx][yy] = EL_FLAMES;
8518
8519             if (IN_SCR_FIELD(sx, sy))
8520             {
8521               TEST_DrawLevelFieldCrumbled(xx, yy);
8522               DrawGraphic(sx, sy, flame_graphic, frame);
8523             }
8524           }
8525           else
8526           {
8527             if (Feld[xx][yy] == EL_FLAMES)
8528               Feld[xx][yy] = EL_EMPTY;
8529             TEST_DrawLevelField(xx, yy);
8530           }
8531         }
8532       }
8533
8534       if (MovDelay[x][y])       /* element still has to wait some time */
8535       {
8536         PlayLevelSoundAction(x, y, ACTION_WAITING);
8537
8538         return;
8539       }
8540     }
8541
8542     /* now make next step */
8543
8544     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8545
8546     if (DONT_COLLIDE_WITH(element) &&
8547         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8548         !PLAYER_ENEMY_PROTECTED(newx, newy))
8549     {
8550       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8551
8552       return;
8553     }
8554
8555     else if (CAN_MOVE_INTO_ACID(element) &&
8556              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8557              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8558              (MovDir[x][y] == MV_DOWN ||
8559               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8560     {
8561       SplashAcid(newx, newy);
8562       Store[x][y] = EL_ACID;
8563     }
8564     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8565     {
8566       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8567           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8568           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8569           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8570       {
8571         RemoveField(x, y);
8572         TEST_DrawLevelField(x, y);
8573
8574         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8575         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8576           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8577
8578         local_player->friends_still_needed--;
8579         if (!local_player->friends_still_needed &&
8580             !local_player->GameOver && AllPlayersGone)
8581           PlayerWins(local_player);
8582
8583         return;
8584       }
8585       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8586       {
8587         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8588           TEST_DrawLevelField(newx, newy);
8589         else
8590           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8591       }
8592       else if (!IS_FREE(newx, newy))
8593       {
8594         GfxAction[x][y] = ACTION_WAITING;
8595
8596         if (IS_PLAYER(x, y))
8597           DrawPlayerField(x, y);
8598         else
8599           TEST_DrawLevelField(x, y);
8600
8601         return;
8602       }
8603     }
8604     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8605     {
8606       if (IS_FOOD_PIG(Feld[newx][newy]))
8607       {
8608         if (IS_MOVING(newx, newy))
8609           RemoveMovingField(newx, newy);
8610         else
8611         {
8612           Feld[newx][newy] = EL_EMPTY;
8613           TEST_DrawLevelField(newx, newy);
8614         }
8615
8616         PlayLevelSound(x, y, SND_PIG_DIGGING);
8617       }
8618       else if (!IS_FREE(newx, newy))
8619       {
8620         if (IS_PLAYER(x, y))
8621           DrawPlayerField(x, y);
8622         else
8623           TEST_DrawLevelField(x, y);
8624
8625         return;
8626       }
8627     }
8628     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8629     {
8630       if (Store[x][y] != EL_EMPTY)
8631       {
8632         boolean can_clone = FALSE;
8633         int xx, yy;
8634
8635         /* check if element to clone is still there */
8636         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8637         {
8638           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8639           {
8640             can_clone = TRUE;
8641
8642             break;
8643           }
8644         }
8645
8646         /* cannot clone or target field not free anymore -- do not clone */
8647         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8648           Store[x][y] = EL_EMPTY;
8649       }
8650
8651       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8652       {
8653         if (IS_MV_DIAGONAL(MovDir[x][y]))
8654         {
8655           int diagonal_move_dir = MovDir[x][y];
8656           int stored = Store[x][y];
8657           int change_delay = 8;
8658           int graphic;
8659
8660           /* android is moving diagonally */
8661
8662           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8663
8664           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8665           GfxElement[x][y] = EL_EMC_ANDROID;
8666           GfxAction[x][y] = ACTION_SHRINKING;
8667           GfxDir[x][y] = diagonal_move_dir;
8668           ChangeDelay[x][y] = change_delay;
8669
8670           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8671                                    GfxDir[x][y]);
8672
8673           DrawLevelGraphicAnimation(x, y, graphic);
8674           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8675
8676           if (Feld[newx][newy] == EL_ACID)
8677           {
8678             SplashAcid(newx, newy);
8679
8680             return;
8681           }
8682
8683           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8684
8685           Store[newx][newy] = EL_EMC_ANDROID;
8686           GfxElement[newx][newy] = EL_EMC_ANDROID;
8687           GfxAction[newx][newy] = ACTION_GROWING;
8688           GfxDir[newx][newy] = diagonal_move_dir;
8689           ChangeDelay[newx][newy] = change_delay;
8690
8691           graphic = el_act_dir2img(GfxElement[newx][newy],
8692                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8693
8694           DrawLevelGraphicAnimation(newx, newy, graphic);
8695           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8696
8697           return;
8698         }
8699         else
8700         {
8701           Feld[newx][newy] = EL_EMPTY;
8702           TEST_DrawLevelField(newx, newy);
8703
8704           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8705         }
8706       }
8707       else if (!IS_FREE(newx, newy))
8708       {
8709 #if 0
8710         if (IS_PLAYER(x, y))
8711           DrawPlayerField(x, y);
8712         else
8713           TEST_DrawLevelField(x, y);
8714 #endif
8715
8716         return;
8717       }
8718     }
8719     else if (IS_CUSTOM_ELEMENT(element) &&
8720              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8721     {
8722 #if 1
8723       if (!DigFieldByCE(newx, newy, element))
8724         return;
8725 #else
8726       int new_element = Feld[newx][newy];
8727
8728       if (!IS_FREE(newx, newy))
8729       {
8730         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8731                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8732                       ACTION_BREAKING);
8733
8734         /* no element can dig solid indestructible elements */
8735         if (IS_INDESTRUCTIBLE(new_element) &&
8736             !IS_DIGGABLE(new_element) &&
8737             !IS_COLLECTIBLE(new_element))
8738           return;
8739
8740         if (AmoebaNr[newx][newy] &&
8741             (new_element == EL_AMOEBA_FULL ||
8742              new_element == EL_BD_AMOEBA ||
8743              new_element == EL_AMOEBA_GROWING))
8744         {
8745           AmoebaCnt[AmoebaNr[newx][newy]]--;
8746           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8747         }
8748
8749         if (IS_MOVING(newx, newy))
8750           RemoveMovingField(newx, newy);
8751         else
8752         {
8753           RemoveField(newx, newy);
8754           TEST_DrawLevelField(newx, newy);
8755         }
8756
8757         /* if digged element was about to explode, prevent the explosion */
8758         ExplodeField[newx][newy] = EX_TYPE_NONE;
8759
8760         PlayLevelSoundAction(x, y, action);
8761       }
8762
8763       Store[newx][newy] = EL_EMPTY;
8764
8765 #if 1
8766       /* this makes it possible to leave the removed element again */
8767       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8768         Store[newx][newy] = new_element;
8769 #else
8770       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8771       {
8772         int move_leave_element = element_info[element].move_leave_element;
8773
8774         /* this makes it possible to leave the removed element again */
8775         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8776                              new_element : move_leave_element);
8777       }
8778 #endif
8779
8780 #endif
8781
8782       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8783       {
8784         RunnerVisit[x][y] = FrameCounter;
8785         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8786       }
8787     }
8788     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8789     {
8790       if (!IS_FREE(newx, newy))
8791       {
8792         if (IS_PLAYER(x, y))
8793           DrawPlayerField(x, y);
8794         else
8795           TEST_DrawLevelField(x, y);
8796
8797         return;
8798       }
8799       else
8800       {
8801         boolean wanna_flame = !RND(10);
8802         int dx = newx - x, dy = newy - y;
8803         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8804         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8805         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8806                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8807         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8808                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8809
8810         if ((wanna_flame ||
8811              IS_CLASSIC_ENEMY(element1) ||
8812              IS_CLASSIC_ENEMY(element2)) &&
8813             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8814             element1 != EL_FLAMES && element2 != EL_FLAMES)
8815         {
8816           ResetGfxAnimation(x, y);
8817           GfxAction[x][y] = ACTION_ATTACKING;
8818
8819           if (IS_PLAYER(x, y))
8820             DrawPlayerField(x, y);
8821           else
8822             TEST_DrawLevelField(x, y);
8823
8824           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8825
8826           MovDelay[x][y] = 50;
8827
8828           /* !!! */
8829 #if 0
8830           RemoveField(newx, newy);
8831 #endif
8832           Feld[newx][newy] = EL_FLAMES;
8833           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8834           {
8835 #if 0
8836             RemoveField(newx1, newy1);
8837 #endif
8838             Feld[newx1][newy1] = EL_FLAMES;
8839           }
8840           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8841           {
8842 #if 0
8843             RemoveField(newx2, newy2);
8844 #endif
8845             Feld[newx2][newy2] = EL_FLAMES;
8846           }
8847
8848           return;
8849         }
8850       }
8851     }
8852     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8853              Feld[newx][newy] == EL_DIAMOND)
8854     {
8855       if (IS_MOVING(newx, newy))
8856         RemoveMovingField(newx, newy);
8857       else
8858       {
8859         Feld[newx][newy] = EL_EMPTY;
8860         TEST_DrawLevelField(newx, newy);
8861       }
8862
8863       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8864     }
8865     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8866              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8867     {
8868       if (AmoebaNr[newx][newy])
8869       {
8870         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8871         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8872             Feld[newx][newy] == EL_BD_AMOEBA)
8873           AmoebaCnt[AmoebaNr[newx][newy]]--;
8874       }
8875
8876 #if 0
8877       /* !!! test !!! */
8878       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8879       {
8880         RemoveMovingField(newx, newy);
8881       }
8882 #else
8883       if (IS_MOVING(newx, newy))
8884       {
8885         RemoveMovingField(newx, newy);
8886       }
8887 #endif
8888       else
8889       {
8890         Feld[newx][newy] = EL_EMPTY;
8891         TEST_DrawLevelField(newx, newy);
8892       }
8893
8894       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8895     }
8896     else if ((element == EL_PACMAN || element == EL_MOLE)
8897              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8898     {
8899       if (AmoebaNr[newx][newy])
8900       {
8901         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8902         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8903             Feld[newx][newy] == EL_BD_AMOEBA)
8904           AmoebaCnt[AmoebaNr[newx][newy]]--;
8905       }
8906
8907       if (element == EL_MOLE)
8908       {
8909         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8910         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8911
8912         ResetGfxAnimation(x, y);
8913         GfxAction[x][y] = ACTION_DIGGING;
8914         TEST_DrawLevelField(x, y);
8915
8916         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8917
8918         return;                         /* wait for shrinking amoeba */
8919       }
8920       else      /* element == EL_PACMAN */
8921       {
8922         Feld[newx][newy] = EL_EMPTY;
8923         TEST_DrawLevelField(newx, newy);
8924         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8925       }
8926     }
8927     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8928              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8929               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8930     {
8931       /* wait for shrinking amoeba to completely disappear */
8932       return;
8933     }
8934     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8935     {
8936       /* object was running against a wall */
8937
8938       TurnRound(x, y);
8939
8940 #if 0
8941       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8942       if (move_pattern & MV_ANY_DIRECTION &&
8943           move_pattern == MovDir[x][y])
8944       {
8945         int blocking_element =
8946           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8947
8948         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8949                                  MovDir[x][y]);
8950
8951         element = Feld[x][y];   /* element might have changed */
8952       }
8953 #endif
8954
8955       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8956         DrawLevelElementAnimation(x, y, element);
8957
8958       if (DONT_TOUCH(element))
8959         TestIfBadThingTouchesPlayer(x, y);
8960
8961       return;
8962     }
8963
8964     InitMovingField(x, y, MovDir[x][y]);
8965
8966     PlayLevelSoundAction(x, y, ACTION_MOVING);
8967   }
8968
8969   if (MovDir[x][y])
8970     ContinueMoving(x, y);
8971 }
8972
8973 void ContinueMoving(int x, int y)
8974 {
8975   int element = Feld[x][y];
8976   struct ElementInfo *ei = &element_info[element];
8977   int direction = MovDir[x][y];
8978   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8979   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8980   int newx = x + dx, newy = y + dy;
8981   int stored = Store[x][y];
8982   int stored_new = Store[newx][newy];
8983   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8984   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8985   boolean last_line = (newy == lev_fieldy - 1);
8986
8987   MovPos[x][y] += getElementMoveStepsize(x, y);
8988
8989   if (pushed_by_player) /* special case: moving object pushed by player */
8990     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8991
8992   if (ABS(MovPos[x][y]) < TILEX)
8993   {
8994 #if 0
8995     int ee = Feld[x][y];
8996     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8997     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8998
8999     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9000            x, y, ABS(MovPos[x][y]),
9001            ee, gg, ff,
9002            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9003 #endif
9004
9005     TEST_DrawLevelField(x, y);
9006
9007     return;     /* element is still moving */
9008   }
9009
9010   /* element reached destination field */
9011
9012   Feld[x][y] = EL_EMPTY;
9013   Feld[newx][newy] = element;
9014   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9015
9016   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9017   {
9018     element = Feld[newx][newy] = EL_ACID;
9019   }
9020   else if (element == EL_MOLE)
9021   {
9022     Feld[x][y] = EL_SAND;
9023
9024     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9025   }
9026   else if (element == EL_QUICKSAND_FILLING)
9027   {
9028     element = Feld[newx][newy] = get_next_element(element);
9029     Store[newx][newy] = Store[x][y];
9030   }
9031   else if (element == EL_QUICKSAND_EMPTYING)
9032   {
9033     Feld[x][y] = get_next_element(element);
9034     element = Feld[newx][newy] = Store[x][y];
9035   }
9036   else if (element == EL_QUICKSAND_FAST_FILLING)
9037   {
9038     element = Feld[newx][newy] = get_next_element(element);
9039     Store[newx][newy] = Store[x][y];
9040   }
9041   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9042   {
9043     Feld[x][y] = get_next_element(element);
9044     element = Feld[newx][newy] = Store[x][y];
9045   }
9046   else if (element == EL_MAGIC_WALL_FILLING)
9047   {
9048     element = Feld[newx][newy] = get_next_element(element);
9049     if (!game.magic_wall_active)
9050       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9051     Store[newx][newy] = Store[x][y];
9052   }
9053   else if (element == EL_MAGIC_WALL_EMPTYING)
9054   {
9055     Feld[x][y] = get_next_element(element);
9056     if (!game.magic_wall_active)
9057       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9058     element = Feld[newx][newy] = Store[x][y];
9059
9060 #if USE_NEW_CUSTOM_VALUE
9061     InitField(newx, newy, FALSE);
9062 #endif
9063   }
9064   else if (element == EL_BD_MAGIC_WALL_FILLING)
9065   {
9066     element = Feld[newx][newy] = get_next_element(element);
9067     if (!game.magic_wall_active)
9068       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9069     Store[newx][newy] = Store[x][y];
9070   }
9071   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9072   {
9073     Feld[x][y] = get_next_element(element);
9074     if (!game.magic_wall_active)
9075       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9076     element = Feld[newx][newy] = Store[x][y];
9077
9078 #if USE_NEW_CUSTOM_VALUE
9079     InitField(newx, newy, FALSE);
9080 #endif
9081   }
9082   else if (element == EL_DC_MAGIC_WALL_FILLING)
9083   {
9084     element = Feld[newx][newy] = get_next_element(element);
9085     if (!game.magic_wall_active)
9086       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9087     Store[newx][newy] = Store[x][y];
9088   }
9089   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9090   {
9091     Feld[x][y] = get_next_element(element);
9092     if (!game.magic_wall_active)
9093       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9094     element = Feld[newx][newy] = Store[x][y];
9095
9096 #if USE_NEW_CUSTOM_VALUE
9097     InitField(newx, newy, FALSE);
9098 #endif
9099   }
9100   else if (element == EL_AMOEBA_DROPPING)
9101   {
9102     Feld[x][y] = get_next_element(element);
9103     element = Feld[newx][newy] = Store[x][y];
9104   }
9105   else if (element == EL_SOKOBAN_OBJECT)
9106   {
9107     if (Back[x][y])
9108       Feld[x][y] = Back[x][y];
9109
9110     if (Back[newx][newy])
9111       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9112
9113     Back[x][y] = Back[newx][newy] = 0;
9114   }
9115
9116   Store[x][y] = EL_EMPTY;
9117   MovPos[x][y] = 0;
9118   MovDir[x][y] = 0;
9119   MovDelay[x][y] = 0;
9120
9121   MovDelay[newx][newy] = 0;
9122
9123   if (CAN_CHANGE_OR_HAS_ACTION(element))
9124   {
9125     /* copy element change control values to new field */
9126     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9127     ChangePage[newx][newy]  = ChangePage[x][y];
9128     ChangeCount[newx][newy] = ChangeCount[x][y];
9129     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9130   }
9131
9132 #if USE_NEW_CUSTOM_VALUE
9133   CustomValue[newx][newy] = CustomValue[x][y];
9134 #endif
9135
9136   ChangeDelay[x][y] = 0;
9137   ChangePage[x][y] = -1;
9138   ChangeCount[x][y] = 0;
9139   ChangeEvent[x][y] = -1;
9140
9141 #if USE_NEW_CUSTOM_VALUE
9142   CustomValue[x][y] = 0;
9143 #endif
9144
9145   /* copy animation control values to new field */
9146   GfxFrame[newx][newy]  = GfxFrame[x][y];
9147   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9148   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9149   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9150
9151   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9152
9153   /* some elements can leave other elements behind after moving */
9154 #if 1
9155   if (ei->move_leave_element != EL_EMPTY &&
9156       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9157       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9158 #else
9159   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9160       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9161       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9162 #endif
9163   {
9164     int move_leave_element = ei->move_leave_element;
9165
9166 #if 1
9167 #if 1
9168     /* this makes it possible to leave the removed element again */
9169     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9170       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9171 #else
9172     /* this makes it possible to leave the removed element again */
9173     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9174       move_leave_element = stored;
9175 #endif
9176 #else
9177     /* this makes it possible to leave the removed element again */
9178     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9179         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9180       move_leave_element = stored;
9181 #endif
9182
9183     Feld[x][y] = move_leave_element;
9184
9185     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9186       MovDir[x][y] = direction;
9187
9188     InitField(x, y, FALSE);
9189
9190     if (GFX_CRUMBLED(Feld[x][y]))
9191       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9192
9193     if (ELEM_IS_PLAYER(move_leave_element))
9194       RelocatePlayer(x, y, move_leave_element);
9195   }
9196
9197   /* do this after checking for left-behind element */
9198   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9199
9200   if (!CAN_MOVE(element) ||
9201       (CAN_FALL(element) && direction == MV_DOWN &&
9202        (element == EL_SPRING ||
9203         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9204         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9205     GfxDir[x][y] = MovDir[newx][newy] = 0;
9206
9207   TEST_DrawLevelField(x, y);
9208   TEST_DrawLevelField(newx, newy);
9209
9210   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9211
9212   /* prevent pushed element from moving on in pushed direction */
9213   if (pushed_by_player && CAN_MOVE(element) &&
9214       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9215       !(element_info[element].move_pattern & direction))
9216     TurnRound(newx, newy);
9217
9218   /* prevent elements on conveyor belt from moving on in last direction */
9219   if (pushed_by_conveyor && CAN_FALL(element) &&
9220       direction & MV_HORIZONTAL)
9221     MovDir[newx][newy] = 0;
9222
9223   if (!pushed_by_player)
9224   {
9225     int nextx = newx + dx, nexty = newy + dy;
9226     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9227
9228     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9229
9230     if (CAN_FALL(element) && direction == MV_DOWN)
9231       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9232
9233     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9234       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9235
9236 #if USE_FIX_IMPACT_COLLISION
9237     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9238       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9239 #endif
9240   }
9241
9242   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9243   {
9244     TestIfBadThingTouchesPlayer(newx, newy);
9245     TestIfBadThingTouchesFriend(newx, newy);
9246
9247     if (!IS_CUSTOM_ELEMENT(element))
9248       TestIfBadThingTouchesOtherBadThing(newx, newy);
9249   }
9250   else if (element == EL_PENGUIN)
9251     TestIfFriendTouchesBadThing(newx, newy);
9252
9253   if (DONT_GET_HIT_BY(element))
9254   {
9255     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9256   }
9257
9258   /* give the player one last chance (one more frame) to move away */
9259   if (CAN_FALL(element) && direction == MV_DOWN &&
9260       (last_line || (!IS_FREE(x, newy + 1) &&
9261                      (!IS_PLAYER(x, newy + 1) ||
9262                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9263     Impact(x, newy);
9264
9265   if (pushed_by_player && !game.use_change_when_pushing_bug)
9266   {
9267     int push_side = MV_DIR_OPPOSITE(direction);
9268     struct PlayerInfo *player = PLAYERINFO(x, y);
9269
9270     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9271                                player->index_bit, push_side);
9272     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9273                                         player->index_bit, push_side);
9274   }
9275
9276   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9277     MovDelay[newx][newy] = 1;
9278
9279   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9280
9281   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9282
9283 #if 0
9284   if (ChangePage[newx][newy] != -1)             /* delayed change */
9285   {
9286     int page = ChangePage[newx][newy];
9287     struct ElementChangeInfo *change = &ei->change_page[page];
9288
9289     ChangePage[newx][newy] = -1;
9290
9291     if (change->can_change)
9292     {
9293       if (ChangeElement(newx, newy, element, page))
9294       {
9295         if (change->post_change_function)
9296           change->post_change_function(newx, newy);
9297       }
9298     }
9299
9300     if (change->has_action)
9301       ExecuteCustomElementAction(newx, newy, element, page);
9302   }
9303 #endif
9304
9305   TestIfElementHitsCustomElement(newx, newy, direction);
9306   TestIfPlayerTouchesCustomElement(newx, newy);
9307   TestIfElementTouchesCustomElement(newx, newy);
9308
9309   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9310       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9311     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9312                              MV_DIR_OPPOSITE(direction));
9313 }
9314
9315 int AmoebeNachbarNr(int ax, int ay)
9316 {
9317   int i;
9318   int element = Feld[ax][ay];
9319   int group_nr = 0;
9320   static int xy[4][2] =
9321   {
9322     { 0, -1 },
9323     { -1, 0 },
9324     { +1, 0 },
9325     { 0, +1 }
9326   };
9327
9328   for (i = 0; i < NUM_DIRECTIONS; i++)
9329   {
9330     int x = ax + xy[i][0];
9331     int y = ay + xy[i][1];
9332
9333     if (!IN_LEV_FIELD(x, y))
9334       continue;
9335
9336     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9337       group_nr = AmoebaNr[x][y];
9338   }
9339
9340   return group_nr;
9341 }
9342
9343 void AmoebenVereinigen(int ax, int ay)
9344 {
9345   int i, x, y, xx, yy;
9346   int new_group_nr = AmoebaNr[ax][ay];
9347   static int xy[4][2] =
9348   {
9349     { 0, -1 },
9350     { -1, 0 },
9351     { +1, 0 },
9352     { 0, +1 }
9353   };
9354
9355   if (new_group_nr == 0)
9356     return;
9357
9358   for (i = 0; i < NUM_DIRECTIONS; i++)
9359   {
9360     x = ax + xy[i][0];
9361     y = ay + xy[i][1];
9362
9363     if (!IN_LEV_FIELD(x, y))
9364       continue;
9365
9366     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9367          Feld[x][y] == EL_BD_AMOEBA ||
9368          Feld[x][y] == EL_AMOEBA_DEAD) &&
9369         AmoebaNr[x][y] != new_group_nr)
9370     {
9371       int old_group_nr = AmoebaNr[x][y];
9372
9373       if (old_group_nr == 0)
9374         return;
9375
9376       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9377       AmoebaCnt[old_group_nr] = 0;
9378       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9379       AmoebaCnt2[old_group_nr] = 0;
9380
9381       SCAN_PLAYFIELD(xx, yy)
9382       {
9383         if (AmoebaNr[xx][yy] == old_group_nr)
9384           AmoebaNr[xx][yy] = new_group_nr;
9385       }
9386     }
9387   }
9388 }
9389
9390 void AmoebeUmwandeln(int ax, int ay)
9391 {
9392   int i, x, y;
9393
9394   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9395   {
9396     int group_nr = AmoebaNr[ax][ay];
9397
9398 #ifdef DEBUG
9399     if (group_nr == 0)
9400     {
9401       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9402       printf("AmoebeUmwandeln(): This should never happen!\n");
9403       return;
9404     }
9405 #endif
9406
9407     SCAN_PLAYFIELD(x, y)
9408     {
9409       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9410       {
9411         AmoebaNr[x][y] = 0;
9412         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9413       }
9414     }
9415
9416     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9417                             SND_AMOEBA_TURNING_TO_GEM :
9418                             SND_AMOEBA_TURNING_TO_ROCK));
9419     Bang(ax, ay);
9420   }
9421   else
9422   {
9423     static int xy[4][2] =
9424     {
9425       { 0, -1 },
9426       { -1, 0 },
9427       { +1, 0 },
9428       { 0, +1 }
9429     };
9430
9431     for (i = 0; i < NUM_DIRECTIONS; i++)
9432     {
9433       x = ax + xy[i][0];
9434       y = ay + xy[i][1];
9435
9436       if (!IN_LEV_FIELD(x, y))
9437         continue;
9438
9439       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9440       {
9441         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9442                               SND_AMOEBA_TURNING_TO_GEM :
9443                               SND_AMOEBA_TURNING_TO_ROCK));
9444         Bang(x, y);
9445       }
9446     }
9447   }
9448 }
9449
9450 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9451 {
9452   int x, y;
9453   int group_nr = AmoebaNr[ax][ay];
9454   boolean done = FALSE;
9455
9456 #ifdef DEBUG
9457   if (group_nr == 0)
9458   {
9459     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9460     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9461     return;
9462   }
9463 #endif
9464
9465   SCAN_PLAYFIELD(x, y)
9466   {
9467     if (AmoebaNr[x][y] == group_nr &&
9468         (Feld[x][y] == EL_AMOEBA_DEAD ||
9469          Feld[x][y] == EL_BD_AMOEBA ||
9470          Feld[x][y] == EL_AMOEBA_GROWING))
9471     {
9472       AmoebaNr[x][y] = 0;
9473       Feld[x][y] = new_element;
9474       InitField(x, y, FALSE);
9475       TEST_DrawLevelField(x, y);
9476       done = TRUE;
9477     }
9478   }
9479
9480   if (done)
9481     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9482                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9483                             SND_BD_AMOEBA_TURNING_TO_GEM));
9484 }
9485
9486 void AmoebeWaechst(int x, int y)
9487 {
9488   static unsigned long sound_delay = 0;
9489   static unsigned long sound_delay_value = 0;
9490
9491   if (!MovDelay[x][y])          /* start new growing cycle */
9492   {
9493     MovDelay[x][y] = 7;
9494
9495     if (DelayReached(&sound_delay, sound_delay_value))
9496     {
9497       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9498       sound_delay_value = 30;
9499     }
9500   }
9501
9502   if (MovDelay[x][y])           /* wait some time before growing bigger */
9503   {
9504     MovDelay[x][y]--;
9505     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9506     {
9507       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9508                                            6 - MovDelay[x][y]);
9509
9510       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9511     }
9512
9513     if (!MovDelay[x][y])
9514     {
9515       Feld[x][y] = Store[x][y];
9516       Store[x][y] = 0;
9517       TEST_DrawLevelField(x, y);
9518     }
9519   }
9520 }
9521
9522 void AmoebaDisappearing(int x, int y)
9523 {
9524   static unsigned long sound_delay = 0;
9525   static unsigned long sound_delay_value = 0;
9526
9527   if (!MovDelay[x][y])          /* start new shrinking cycle */
9528   {
9529     MovDelay[x][y] = 7;
9530
9531     if (DelayReached(&sound_delay, sound_delay_value))
9532       sound_delay_value = 30;
9533   }
9534
9535   if (MovDelay[x][y])           /* wait some time before shrinking */
9536   {
9537     MovDelay[x][y]--;
9538     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9539     {
9540       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9541                                            6 - MovDelay[x][y]);
9542
9543       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9544     }
9545
9546     if (!MovDelay[x][y])
9547     {
9548       Feld[x][y] = EL_EMPTY;
9549       TEST_DrawLevelField(x, y);
9550
9551       /* don't let mole enter this field in this cycle;
9552          (give priority to objects falling to this field from above) */
9553       Stop[x][y] = TRUE;
9554     }
9555   }
9556 }
9557
9558 void AmoebeAbleger(int ax, int ay)
9559 {
9560   int i;
9561   int element = Feld[ax][ay];
9562   int graphic = el2img(element);
9563   int newax = ax, neway = ay;
9564   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9565   static int xy[4][2] =
9566   {
9567     { 0, -1 },
9568     { -1, 0 },
9569     { +1, 0 },
9570     { 0, +1 }
9571   };
9572
9573   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9574   {
9575     Feld[ax][ay] = EL_AMOEBA_DEAD;
9576     TEST_DrawLevelField(ax, ay);
9577     return;
9578   }
9579
9580   if (IS_ANIMATED(graphic))
9581     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9582
9583   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9584     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9585
9586   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9587   {
9588     MovDelay[ax][ay]--;
9589     if (MovDelay[ax][ay])
9590       return;
9591   }
9592
9593   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9594   {
9595     int start = RND(4);
9596     int x = ax + xy[start][0];
9597     int y = ay + xy[start][1];
9598
9599     if (!IN_LEV_FIELD(x, y))
9600       return;
9601
9602     if (IS_FREE(x, y) ||
9603         CAN_GROW_INTO(Feld[x][y]) ||
9604         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9605         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9606     {
9607       newax = x;
9608       neway = y;
9609     }
9610
9611     if (newax == ax && neway == ay)
9612       return;
9613   }
9614   else                          /* normal or "filled" (BD style) amoeba */
9615   {
9616     int start = RND(4);
9617     boolean waiting_for_player = FALSE;
9618
9619     for (i = 0; i < NUM_DIRECTIONS; i++)
9620     {
9621       int j = (start + i) % 4;
9622       int x = ax + xy[j][0];
9623       int y = ay + xy[j][1];
9624
9625       if (!IN_LEV_FIELD(x, y))
9626         continue;
9627
9628       if (IS_FREE(x, y) ||
9629           CAN_GROW_INTO(Feld[x][y]) ||
9630           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9631           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9632       {
9633         newax = x;
9634         neway = y;
9635         break;
9636       }
9637       else if (IS_PLAYER(x, y))
9638         waiting_for_player = TRUE;
9639     }
9640
9641     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9642     {
9643       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9644       {
9645         Feld[ax][ay] = EL_AMOEBA_DEAD;
9646         TEST_DrawLevelField(ax, ay);
9647         AmoebaCnt[AmoebaNr[ax][ay]]--;
9648
9649         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9650         {
9651           if (element == EL_AMOEBA_FULL)
9652             AmoebeUmwandeln(ax, ay);
9653           else if (element == EL_BD_AMOEBA)
9654             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9655         }
9656       }
9657       return;
9658     }
9659     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9660     {
9661       /* amoeba gets larger by growing in some direction */
9662
9663       int new_group_nr = AmoebaNr[ax][ay];
9664
9665 #ifdef DEBUG
9666   if (new_group_nr == 0)
9667   {
9668     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9669     printf("AmoebeAbleger(): This should never happen!\n");
9670     return;
9671   }
9672 #endif
9673
9674       AmoebaNr[newax][neway] = new_group_nr;
9675       AmoebaCnt[new_group_nr]++;
9676       AmoebaCnt2[new_group_nr]++;
9677
9678       /* if amoeba touches other amoeba(s) after growing, unify them */
9679       AmoebenVereinigen(newax, neway);
9680
9681       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9682       {
9683         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9684         return;
9685       }
9686     }
9687   }
9688
9689   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9690       (neway == lev_fieldy - 1 && newax != ax))
9691   {
9692     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9693     Store[newax][neway] = element;
9694   }
9695   else if (neway == ay || element == EL_EMC_DRIPPER)
9696   {
9697     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9698
9699     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9700   }
9701   else
9702   {
9703     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9704     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9705     Store[ax][ay] = EL_AMOEBA_DROP;
9706     ContinueMoving(ax, ay);
9707     return;
9708   }
9709
9710   TEST_DrawLevelField(newax, neway);
9711 }
9712
9713 void Life(int ax, int ay)
9714 {
9715   int x1, y1, x2, y2;
9716   int life_time = 40;
9717   int element = Feld[ax][ay];
9718   int graphic = el2img(element);
9719   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9720                          level.biomaze);
9721   boolean changed = FALSE;
9722
9723   if (IS_ANIMATED(graphic))
9724     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9725
9726   if (Stop[ax][ay])
9727     return;
9728
9729   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9730     MovDelay[ax][ay] = life_time;
9731
9732   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9733   {
9734     MovDelay[ax][ay]--;
9735     if (MovDelay[ax][ay])
9736       return;
9737   }
9738
9739   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9740   {
9741     int xx = ax+x1, yy = ay+y1;
9742     int nachbarn = 0;
9743
9744     if (!IN_LEV_FIELD(xx, yy))
9745       continue;
9746
9747     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9748     {
9749       int x = xx+x2, y = yy+y2;
9750
9751       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9752         continue;
9753
9754       if (((Feld[x][y] == element ||
9755             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9756            !Stop[x][y]) ||
9757           (IS_FREE(x, y) && Stop[x][y]))
9758         nachbarn++;
9759     }
9760
9761     if (xx == ax && yy == ay)           /* field in the middle */
9762     {
9763       if (nachbarn < life_parameter[0] ||
9764           nachbarn > life_parameter[1])
9765       {
9766         Feld[xx][yy] = EL_EMPTY;
9767         if (!Stop[xx][yy])
9768           TEST_DrawLevelField(xx, yy);
9769         Stop[xx][yy] = TRUE;
9770         changed = TRUE;
9771       }
9772     }
9773     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9774     {                                   /* free border field */
9775       if (nachbarn >= life_parameter[2] &&
9776           nachbarn <= life_parameter[3])
9777       {
9778         Feld[xx][yy] = element;
9779         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9780         if (!Stop[xx][yy])
9781           TEST_DrawLevelField(xx, yy);
9782         Stop[xx][yy] = TRUE;
9783         changed = TRUE;
9784       }
9785     }
9786   }
9787
9788   if (changed)
9789     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9790                    SND_GAME_OF_LIFE_GROWING);
9791 }
9792
9793 static void InitRobotWheel(int x, int y)
9794 {
9795   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9796 }
9797
9798 static void RunRobotWheel(int x, int y)
9799 {
9800   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9801 }
9802
9803 static void StopRobotWheel(int x, int y)
9804 {
9805   if (ZX == x && ZY == y)
9806   {
9807     ZX = ZY = -1;
9808
9809     game.robot_wheel_active = FALSE;
9810   }
9811 }
9812
9813 static void InitTimegateWheel(int x, int y)
9814 {
9815   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9816 }
9817
9818 static void RunTimegateWheel(int x, int y)
9819 {
9820   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9821 }
9822
9823 static void InitMagicBallDelay(int x, int y)
9824 {
9825 #if 1
9826   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9827 #else
9828   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9829 #endif
9830 }
9831
9832 static void ActivateMagicBall(int bx, int by)
9833 {
9834   int x, y;
9835
9836   if (level.ball_random)
9837   {
9838     int pos_border = RND(8);    /* select one of the eight border elements */
9839     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9840     int xx = pos_content % 3;
9841     int yy = pos_content / 3;
9842
9843     x = bx - 1 + xx;
9844     y = by - 1 + yy;
9845
9846     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9847       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9848   }
9849   else
9850   {
9851     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9852     {
9853       int xx = x - bx + 1;
9854       int yy = y - by + 1;
9855
9856       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9857         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9858     }
9859   }
9860
9861   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9862 }
9863
9864 void CheckExit(int x, int y)
9865 {
9866   if (local_player->gems_still_needed > 0 ||
9867       local_player->sokobanfields_still_needed > 0 ||
9868       local_player->lights_still_needed > 0)
9869   {
9870     int element = Feld[x][y];
9871     int graphic = el2img(element);
9872
9873     if (IS_ANIMATED(graphic))
9874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9875
9876     return;
9877   }
9878
9879   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9880     return;
9881
9882   Feld[x][y] = EL_EXIT_OPENING;
9883
9884   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9885 }
9886
9887 void CheckExitEM(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_EM_EXIT_OPENING;
9906
9907   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9908 }
9909
9910 void CheckExitSteel(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_STEEL_EXIT_OPENING;
9929
9930   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9931 }
9932
9933 void CheckExitSteelEM(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_EM_STEEL_EXIT_OPENING;
9952
9953   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9954 }
9955
9956 void CheckExitSP(int x, int y)
9957 {
9958   if (local_player->gems_still_needed > 0)
9959   {
9960     int element = Feld[x][y];
9961     int graphic = el2img(element);
9962
9963     if (IS_ANIMATED(graphic))
9964       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9965
9966     return;
9967   }
9968
9969   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9970     return;
9971
9972   Feld[x][y] = EL_SP_EXIT_OPENING;
9973
9974   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9975 }
9976
9977 static void CloseAllOpenTimegates()
9978 {
9979   int x, y;
9980
9981   SCAN_PLAYFIELD(x, y)
9982   {
9983     int element = Feld[x][y];
9984
9985     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9986     {
9987       Feld[x][y] = EL_TIMEGATE_CLOSING;
9988
9989       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9990     }
9991   }
9992 }
9993
9994 void DrawTwinkleOnField(int x, int y)
9995 {
9996   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9997     return;
9998
9999   if (Feld[x][y] == EL_BD_DIAMOND)
10000     return;
10001
10002   if (MovDelay[x][y] == 0)      /* next animation frame */
10003     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10004
10005   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10006   {
10007     MovDelay[x][y]--;
10008
10009     DrawLevelElementAnimation(x, y, Feld[x][y]);
10010
10011     if (MovDelay[x][y] != 0)
10012     {
10013       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10014                                            10 - MovDelay[x][y]);
10015
10016       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10017     }
10018   }
10019 }
10020
10021 void MauerWaechst(int x, int y)
10022 {
10023   int delay = 6;
10024
10025   if (!MovDelay[x][y])          /* next animation frame */
10026     MovDelay[x][y] = 3 * delay;
10027
10028   if (MovDelay[x][y])           /* wait some time before next frame */
10029   {
10030     MovDelay[x][y]--;
10031
10032     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10033     {
10034       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10035       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10036
10037       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10038     }
10039
10040     if (!MovDelay[x][y])
10041     {
10042       if (MovDir[x][y] == MV_LEFT)
10043       {
10044         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10045           TEST_DrawLevelField(x - 1, y);
10046       }
10047       else if (MovDir[x][y] == MV_RIGHT)
10048       {
10049         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10050           TEST_DrawLevelField(x + 1, y);
10051       }
10052       else if (MovDir[x][y] == MV_UP)
10053       {
10054         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10055           TEST_DrawLevelField(x, y - 1);
10056       }
10057       else
10058       {
10059         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10060           TEST_DrawLevelField(x, y + 1);
10061       }
10062
10063       Feld[x][y] = Store[x][y];
10064       Store[x][y] = 0;
10065       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10066       TEST_DrawLevelField(x, y);
10067     }
10068   }
10069 }
10070
10071 void MauerAbleger(int ax, int ay)
10072 {
10073   int element = Feld[ax][ay];
10074   int graphic = el2img(element);
10075   boolean oben_frei = FALSE, unten_frei = FALSE;
10076   boolean links_frei = FALSE, rechts_frei = FALSE;
10077   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10078   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10079   boolean new_wall = FALSE;
10080
10081   if (IS_ANIMATED(graphic))
10082     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10083
10084   if (!MovDelay[ax][ay])        /* start building new wall */
10085     MovDelay[ax][ay] = 6;
10086
10087   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10088   {
10089     MovDelay[ax][ay]--;
10090     if (MovDelay[ax][ay])
10091       return;
10092   }
10093
10094   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10095     oben_frei = TRUE;
10096   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10097     unten_frei = TRUE;
10098   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10099     links_frei = TRUE;
10100   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10101     rechts_frei = TRUE;
10102
10103   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10104       element == EL_EXPANDABLE_WALL_ANY)
10105   {
10106     if (oben_frei)
10107     {
10108       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10109       Store[ax][ay-1] = element;
10110       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10111       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10112         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10113                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10114       new_wall = TRUE;
10115     }
10116     if (unten_frei)
10117     {
10118       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10119       Store[ax][ay+1] = element;
10120       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10121       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10122         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10123                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10124       new_wall = TRUE;
10125     }
10126   }
10127
10128   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10129       element == EL_EXPANDABLE_WALL_ANY ||
10130       element == EL_EXPANDABLE_WALL ||
10131       element == EL_BD_EXPANDABLE_WALL)
10132   {
10133     if (links_frei)
10134     {
10135       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136       Store[ax-1][ay] = element;
10137       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10138       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10139         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10140                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10141       new_wall = TRUE;
10142     }
10143
10144     if (rechts_frei)
10145     {
10146       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10147       Store[ax+1][ay] = element;
10148       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10149       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10150         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10151                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10152       new_wall = TRUE;
10153     }
10154   }
10155
10156   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10157     TEST_DrawLevelField(ax, ay);
10158
10159   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10160     oben_massiv = TRUE;
10161   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10162     unten_massiv = TRUE;
10163   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10164     links_massiv = TRUE;
10165   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10166     rechts_massiv = TRUE;
10167
10168   if (((oben_massiv && unten_massiv) ||
10169        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10170        element == EL_EXPANDABLE_WALL) &&
10171       ((links_massiv && rechts_massiv) ||
10172        element == EL_EXPANDABLE_WALL_VERTICAL))
10173     Feld[ax][ay] = EL_WALL;
10174
10175   if (new_wall)
10176     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10177 }
10178
10179 void MauerAblegerStahl(int ax, int ay)
10180 {
10181   int element = Feld[ax][ay];
10182   int graphic = el2img(element);
10183   boolean oben_frei = FALSE, unten_frei = FALSE;
10184   boolean links_frei = FALSE, rechts_frei = FALSE;
10185   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10186   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10187   boolean new_wall = FALSE;
10188
10189   if (IS_ANIMATED(graphic))
10190     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10191
10192   if (!MovDelay[ax][ay])        /* start building new wall */
10193     MovDelay[ax][ay] = 6;
10194
10195   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10196   {
10197     MovDelay[ax][ay]--;
10198     if (MovDelay[ax][ay])
10199       return;
10200   }
10201
10202   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10203     oben_frei = TRUE;
10204   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10205     unten_frei = TRUE;
10206   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10207     links_frei = TRUE;
10208   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10209     rechts_frei = TRUE;
10210
10211   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10212       element == EL_EXPANDABLE_STEELWALL_ANY)
10213   {
10214     if (oben_frei)
10215     {
10216       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10217       Store[ax][ay-1] = element;
10218       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10219       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10220         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10221                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10222       new_wall = TRUE;
10223     }
10224     if (unten_frei)
10225     {
10226       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10227       Store[ax][ay+1] = element;
10228       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10229       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10230         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10231                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10232       new_wall = TRUE;
10233     }
10234   }
10235
10236   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10237       element == EL_EXPANDABLE_STEELWALL_ANY)
10238   {
10239     if (links_frei)
10240     {
10241       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242       Store[ax-1][ay] = element;
10243       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10244       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10245         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10246                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10247       new_wall = TRUE;
10248     }
10249
10250     if (rechts_frei)
10251     {
10252       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10253       Store[ax+1][ay] = element;
10254       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10255       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10256         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10257                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10258       new_wall = TRUE;
10259     }
10260   }
10261
10262   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10263     oben_massiv = TRUE;
10264   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10265     unten_massiv = TRUE;
10266   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10267     links_massiv = TRUE;
10268   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10269     rechts_massiv = TRUE;
10270
10271   if (((oben_massiv && unten_massiv) ||
10272        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10273       ((links_massiv && rechts_massiv) ||
10274        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10275     Feld[ax][ay] = EL_STEELWALL;
10276
10277   if (new_wall)
10278     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10279 }
10280
10281 void CheckForDragon(int x, int y)
10282 {
10283   int i, j;
10284   boolean dragon_found = FALSE;
10285   static int xy[4][2] =
10286   {
10287     { 0, -1 },
10288     { -1, 0 },
10289     { +1, 0 },
10290     { 0, +1 }
10291   };
10292
10293   for (i = 0; i < NUM_DIRECTIONS; i++)
10294   {
10295     for (j = 0; j < 4; j++)
10296     {
10297       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10298
10299       if (IN_LEV_FIELD(xx, yy) &&
10300           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10301       {
10302         if (Feld[xx][yy] == EL_DRAGON)
10303           dragon_found = TRUE;
10304       }
10305       else
10306         break;
10307     }
10308   }
10309
10310   if (!dragon_found)
10311   {
10312     for (i = 0; i < NUM_DIRECTIONS; i++)
10313     {
10314       for (j = 0; j < 3; j++)
10315       {
10316         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10317   
10318         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10319         {
10320           Feld[xx][yy] = EL_EMPTY;
10321           TEST_DrawLevelField(xx, yy);
10322         }
10323         else
10324           break;
10325       }
10326     }
10327   }
10328 }
10329
10330 static void InitBuggyBase(int x, int y)
10331 {
10332   int element = Feld[x][y];
10333   int activating_delay = FRAMES_PER_SECOND / 4;
10334
10335   ChangeDelay[x][y] =
10336     (element == EL_SP_BUGGY_BASE ?
10337      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10338      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10339      activating_delay :
10340      element == EL_SP_BUGGY_BASE_ACTIVE ?
10341      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10342 }
10343
10344 static void WarnBuggyBase(int x, int y)
10345 {
10346   int i;
10347   static int xy[4][2] =
10348   {
10349     { 0, -1 },
10350     { -1, 0 },
10351     { +1, 0 },
10352     { 0, +1 }
10353   };
10354
10355   for (i = 0; i < NUM_DIRECTIONS; i++)
10356   {
10357     int xx = x + xy[i][0];
10358     int yy = y + xy[i][1];
10359
10360     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10361     {
10362       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10363
10364       break;
10365     }
10366   }
10367 }
10368
10369 static void InitTrap(int x, int y)
10370 {
10371   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10372 }
10373
10374 static void ActivateTrap(int x, int y)
10375 {
10376   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10377 }
10378
10379 static void ChangeActiveTrap(int x, int y)
10380 {
10381   int graphic = IMG_TRAP_ACTIVE;
10382
10383   /* if new animation frame was drawn, correct crumbled sand border */
10384   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10385     TEST_DrawLevelFieldCrumbled(x, y);
10386 }
10387
10388 static int getSpecialActionElement(int element, int number, int base_element)
10389 {
10390   return (element != EL_EMPTY ? element :
10391           number != -1 ? base_element + number - 1 :
10392           EL_EMPTY);
10393 }
10394
10395 static int getModifiedActionNumber(int value_old, int operator, int operand,
10396                                    int value_min, int value_max)
10397 {
10398   int value_new = (operator == CA_MODE_SET      ? operand :
10399                    operator == CA_MODE_ADD      ? value_old + operand :
10400                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10401                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10402                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10403                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10404                    value_old);
10405
10406   return (value_new < value_min ? value_min :
10407           value_new > value_max ? value_max :
10408           value_new);
10409 }
10410
10411 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10412 {
10413   struct ElementInfo *ei = &element_info[element];
10414   struct ElementChangeInfo *change = &ei->change_page[page];
10415   int target_element = change->target_element;
10416   int action_type = change->action_type;
10417   int action_mode = change->action_mode;
10418   int action_arg = change->action_arg;
10419   int action_element = change->action_element;
10420   int i;
10421
10422   if (!change->has_action)
10423     return;
10424
10425   /* ---------- determine action paramater values -------------------------- */
10426
10427   int level_time_value =
10428     (level.time > 0 ? TimeLeft :
10429      TimePlayed);
10430
10431   int action_arg_element_raw =
10432     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10433      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10434      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10435      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10436      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10437      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10438      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10439      EL_EMPTY);
10440   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10441
10442 #if 0
10443   if (action_arg_element_raw == EL_GROUP_START)
10444     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10445 #endif
10446
10447   int action_arg_direction =
10448     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10449      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10450      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10451      change->actual_trigger_side :
10452      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10453      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10454      MV_NONE);
10455
10456   int action_arg_number_min =
10457     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10458      CA_ARG_MIN);
10459
10460   int action_arg_number_max =
10461     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10462      action_type == CA_SET_LEVEL_GEMS ? 999 :
10463      action_type == CA_SET_LEVEL_TIME ? 9999 :
10464      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10465      action_type == CA_SET_CE_VALUE ? 9999 :
10466      action_type == CA_SET_CE_SCORE ? 9999 :
10467      CA_ARG_MAX);
10468
10469   int action_arg_number_reset =
10470     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10471      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10472      action_type == CA_SET_LEVEL_TIME ? level.time :
10473      action_type == CA_SET_LEVEL_SCORE ? 0 :
10474 #if USE_NEW_CUSTOM_VALUE
10475      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10476 #else
10477      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10478 #endif
10479      action_type == CA_SET_CE_SCORE ? 0 :
10480      0);
10481
10482   int action_arg_number =
10483     (action_arg <= CA_ARG_MAX ? action_arg :
10484      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10485      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10486      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10487      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10488      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10489      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10490 #if USE_NEW_CUSTOM_VALUE
10491      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10492 #else
10493      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10494 #endif
10495      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10496      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10497      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10498      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10499      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10500      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10501      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10502      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10503      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10504      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10505      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10506      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10507      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10508      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10509      -1);
10510
10511   int action_arg_number_old =
10512     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10513      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10514      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10515      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10516      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10517      0);
10518
10519   int action_arg_number_new =
10520     getModifiedActionNumber(action_arg_number_old,
10521                             action_mode, action_arg_number,
10522                             action_arg_number_min, action_arg_number_max);
10523
10524 #if 1
10525   int trigger_player_bits =
10526     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10527      change->actual_trigger_player_bits : change->trigger_player);
10528 #else
10529   int trigger_player_bits =
10530     (change->actual_trigger_player >= EL_PLAYER_1 &&
10531      change->actual_trigger_player <= EL_PLAYER_4 ?
10532      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10533      PLAYER_BITS_ANY);
10534 #endif
10535
10536   int action_arg_player_bits =
10537     (action_arg >= CA_ARG_PLAYER_1 &&
10538      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10539      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10540      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10541      PLAYER_BITS_ANY);
10542
10543   /* ---------- execute action  -------------------------------------------- */
10544
10545   switch (action_type)
10546   {
10547     case CA_NO_ACTION:
10548     {
10549       return;
10550     }
10551
10552     /* ---------- level actions  ------------------------------------------- */
10553
10554     case CA_RESTART_LEVEL:
10555     {
10556       game.restart_level = TRUE;
10557
10558       break;
10559     }
10560
10561     case CA_SHOW_ENVELOPE:
10562     {
10563       int element = getSpecialActionElement(action_arg_element,
10564                                             action_arg_number, EL_ENVELOPE_1);
10565
10566       if (IS_ENVELOPE(element))
10567         local_player->show_envelope = element;
10568
10569       break;
10570     }
10571
10572     case CA_SET_LEVEL_TIME:
10573     {
10574       if (level.time > 0)       /* only modify limited time value */
10575       {
10576         TimeLeft = action_arg_number_new;
10577
10578 #if 1
10579         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10580
10581         DisplayGameControlValues();
10582 #else
10583         DrawGameValue_Time(TimeLeft);
10584 #endif
10585
10586         if (!TimeLeft && setup.time_limit)
10587           for (i = 0; i < MAX_PLAYERS; i++)
10588             KillPlayer(&stored_player[i]);
10589       }
10590
10591       break;
10592     }
10593
10594     case CA_SET_LEVEL_SCORE:
10595     {
10596       local_player->score = action_arg_number_new;
10597
10598 #if 1
10599       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10600
10601       DisplayGameControlValues();
10602 #else
10603       DrawGameValue_Score(local_player->score);
10604 #endif
10605
10606       break;
10607     }
10608
10609     case CA_SET_LEVEL_GEMS:
10610     {
10611       local_player->gems_still_needed = action_arg_number_new;
10612
10613 #if 1
10614       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10615
10616       DisplayGameControlValues();
10617 #else
10618       DrawGameValue_Emeralds(local_player->gems_still_needed);
10619 #endif
10620
10621       break;
10622     }
10623
10624 #if !USE_PLAYER_GRAVITY
10625     case CA_SET_LEVEL_GRAVITY:
10626     {
10627       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10628                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10629                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10630                       game.gravity);
10631       break;
10632     }
10633 #endif
10634
10635     case CA_SET_LEVEL_WIND:
10636     {
10637       game.wind_direction = action_arg_direction;
10638
10639       break;
10640     }
10641
10642     case CA_SET_LEVEL_RANDOM_SEED:
10643     {
10644 #if 1
10645       /* ensure that setting a new random seed while playing is predictable */
10646       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10647 #else
10648       InitRND(action_arg_number_new);
10649 #endif
10650
10651 #if 0
10652       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10653 #endif
10654
10655 #if 0
10656       {
10657         int i;
10658
10659         printf("::: ");
10660         for (i = 0; i < 9; i++)
10661           printf("%d, ", RND(2));
10662         printf("\n");
10663       }
10664 #endif
10665
10666       break;
10667     }
10668
10669     /* ---------- player actions  ------------------------------------------ */
10670
10671     case CA_MOVE_PLAYER:
10672     {
10673       /* automatically move to the next field in specified direction */
10674       for (i = 0; i < MAX_PLAYERS; i++)
10675         if (trigger_player_bits & (1 << i))
10676           stored_player[i].programmed_action = action_arg_direction;
10677
10678       break;
10679     }
10680
10681     case CA_EXIT_PLAYER:
10682     {
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684         if (action_arg_player_bits & (1 << i))
10685           PlayerWins(&stored_player[i]);
10686
10687       break;
10688     }
10689
10690     case CA_KILL_PLAYER:
10691     {
10692       for (i = 0; i < MAX_PLAYERS; i++)
10693         if (action_arg_player_bits & (1 << i))
10694           KillPlayer(&stored_player[i]);
10695
10696       break;
10697     }
10698
10699     case CA_SET_PLAYER_KEYS:
10700     {
10701       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10702       int element = getSpecialActionElement(action_arg_element,
10703                                             action_arg_number, EL_KEY_1);
10704
10705       if (IS_KEY(element))
10706       {
10707         for (i = 0; i < MAX_PLAYERS; i++)
10708         {
10709           if (trigger_player_bits & (1 << i))
10710           {
10711             stored_player[i].key[KEY_NR(element)] = key_state;
10712
10713             DrawGameDoorValues();
10714           }
10715         }
10716       }
10717
10718       break;
10719     }
10720
10721     case CA_SET_PLAYER_SPEED:
10722     {
10723 #if 0
10724       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10725 #endif
10726
10727       for (i = 0; i < MAX_PLAYERS; i++)
10728       {
10729         if (trigger_player_bits & (1 << i))
10730         {
10731           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10732
10733           if (action_arg == CA_ARG_SPEED_FASTER &&
10734               stored_player[i].cannot_move)
10735           {
10736             action_arg_number = STEPSIZE_VERY_SLOW;
10737           }
10738           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10739                    action_arg == CA_ARG_SPEED_FASTER)
10740           {
10741             action_arg_number = 2;
10742             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10743                            CA_MODE_MULTIPLY);
10744           }
10745           else if (action_arg == CA_ARG_NUMBER_RESET)
10746           {
10747             action_arg_number = level.initial_player_stepsize[i];
10748           }
10749
10750           move_stepsize =
10751             getModifiedActionNumber(move_stepsize,
10752                                     action_mode,
10753                                     action_arg_number,
10754                                     action_arg_number_min,
10755                                     action_arg_number_max);
10756
10757           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10758         }
10759       }
10760
10761       break;
10762     }
10763
10764     case CA_SET_PLAYER_SHIELD:
10765     {
10766       for (i = 0; i < MAX_PLAYERS; i++)
10767       {
10768         if (trigger_player_bits & (1 << i))
10769         {
10770           if (action_arg == CA_ARG_SHIELD_OFF)
10771           {
10772             stored_player[i].shield_normal_time_left = 0;
10773             stored_player[i].shield_deadly_time_left = 0;
10774           }
10775           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10776           {
10777             stored_player[i].shield_normal_time_left = 999999;
10778           }
10779           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10780           {
10781             stored_player[i].shield_normal_time_left = 999999;
10782             stored_player[i].shield_deadly_time_left = 999999;
10783           }
10784         }
10785       }
10786
10787       break;
10788     }
10789
10790 #if USE_PLAYER_GRAVITY
10791     case CA_SET_PLAYER_GRAVITY:
10792     {
10793       for (i = 0; i < MAX_PLAYERS; i++)
10794       {
10795         if (trigger_player_bits & (1 << i))
10796         {
10797           stored_player[i].gravity =
10798             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10799              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10800              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10801              stored_player[i].gravity);
10802         }
10803       }
10804
10805       break;
10806     }
10807 #endif
10808
10809     case CA_SET_PLAYER_ARTWORK:
10810     {
10811       for (i = 0; i < MAX_PLAYERS; i++)
10812       {
10813         if (trigger_player_bits & (1 << i))
10814         {
10815           int artwork_element = action_arg_element;
10816
10817           if (action_arg == CA_ARG_ELEMENT_RESET)
10818             artwork_element =
10819               (level.use_artwork_element[i] ? level.artwork_element[i] :
10820                stored_player[i].element_nr);
10821
10822 #if USE_GFX_RESET_PLAYER_ARTWORK
10823           if (stored_player[i].artwork_element != artwork_element)
10824             stored_player[i].Frame = 0;
10825 #endif
10826
10827           stored_player[i].artwork_element = artwork_element;
10828
10829           SetPlayerWaiting(&stored_player[i], FALSE);
10830
10831           /* set number of special actions for bored and sleeping animation */
10832           stored_player[i].num_special_action_bored =
10833             get_num_special_action(artwork_element,
10834                                    ACTION_BORING_1, ACTION_BORING_LAST);
10835           stored_player[i].num_special_action_sleeping =
10836             get_num_special_action(artwork_element,
10837                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10838         }
10839       }
10840
10841       break;
10842     }
10843
10844     case CA_SET_PLAYER_INVENTORY:
10845     {
10846       for (i = 0; i < MAX_PLAYERS; i++)
10847       {
10848         struct PlayerInfo *player = &stored_player[i];
10849         int j, k;
10850
10851         if (trigger_player_bits & (1 << i))
10852         {
10853           int inventory_element = action_arg_element;
10854
10855           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10856               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10857               action_arg == CA_ARG_ELEMENT_ACTION)
10858           {
10859             int element = inventory_element;
10860             int collect_count = element_info[element].collect_count_initial;
10861
10862             if (!IS_CUSTOM_ELEMENT(element))
10863               collect_count = 1;
10864
10865             if (collect_count == 0)
10866               player->inventory_infinite_element = element;
10867             else
10868               for (k = 0; k < collect_count; k++)
10869                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10870                   player->inventory_element[player->inventory_size++] =
10871                     element;
10872           }
10873           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10874                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10875                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10876           {
10877             if (player->inventory_infinite_element != EL_UNDEFINED &&
10878                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10879                                      action_arg_element_raw))
10880               player->inventory_infinite_element = EL_UNDEFINED;
10881
10882             for (k = 0, j = 0; j < player->inventory_size; j++)
10883             {
10884               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10885                                         action_arg_element_raw))
10886                 player->inventory_element[k++] = player->inventory_element[j];
10887             }
10888
10889             player->inventory_size = k;
10890           }
10891           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10892           {
10893             if (player->inventory_size > 0)
10894             {
10895               for (j = 0; j < player->inventory_size - 1; j++)
10896                 player->inventory_element[j] = player->inventory_element[j + 1];
10897
10898               player->inventory_size--;
10899             }
10900           }
10901           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10902           {
10903             if (player->inventory_size > 0)
10904               player->inventory_size--;
10905           }
10906           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10907           {
10908             player->inventory_infinite_element = EL_UNDEFINED;
10909             player->inventory_size = 0;
10910           }
10911           else if (action_arg == CA_ARG_INVENTORY_RESET)
10912           {
10913             player->inventory_infinite_element = EL_UNDEFINED;
10914             player->inventory_size = 0;
10915
10916             if (level.use_initial_inventory[i])
10917             {
10918               for (j = 0; j < level.initial_inventory_size[i]; j++)
10919               {
10920                 int element = level.initial_inventory_content[i][j];
10921                 int collect_count = element_info[element].collect_count_initial;
10922
10923                 if (!IS_CUSTOM_ELEMENT(element))
10924                   collect_count = 1;
10925
10926                 if (collect_count == 0)
10927                   player->inventory_infinite_element = element;
10928                 else
10929                   for (k = 0; k < collect_count; k++)
10930                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10931                       player->inventory_element[player->inventory_size++] =
10932                         element;
10933               }
10934             }
10935           }
10936         }
10937       }
10938
10939       break;
10940     }
10941
10942     /* ---------- CE actions  ---------------------------------------------- */
10943
10944     case CA_SET_CE_VALUE:
10945     {
10946 #if USE_NEW_CUSTOM_VALUE
10947       int last_ce_value = CustomValue[x][y];
10948
10949       CustomValue[x][y] = action_arg_number_new;
10950
10951       if (CustomValue[x][y] != last_ce_value)
10952       {
10953         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10954         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10955
10956         if (CustomValue[x][y] == 0)
10957         {
10958           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10959           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10960         }
10961       }
10962 #endif
10963
10964       break;
10965     }
10966
10967     case CA_SET_CE_SCORE:
10968     {
10969 #if USE_NEW_CUSTOM_VALUE
10970       int last_ce_score = ei->collect_score;
10971
10972       ei->collect_score = action_arg_number_new;
10973
10974       if (ei->collect_score != last_ce_score)
10975       {
10976         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10977         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10978
10979         if (ei->collect_score == 0)
10980         {
10981           int xx, yy;
10982
10983           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10984           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10985
10986           /*
10987             This is a very special case that seems to be a mixture between
10988             CheckElementChange() and CheckTriggeredElementChange(): while
10989             the first one only affects single elements that are triggered
10990             directly, the second one affects multiple elements in the playfield
10991             that are triggered indirectly by another element. This is a third
10992             case: Changing the CE score always affects multiple identical CEs,
10993             so every affected CE must be checked, not only the single CE for
10994             which the CE score was changed in the first place (as every instance
10995             of that CE shares the same CE score, and therefore also can change)!
10996           */
10997           SCAN_PLAYFIELD(xx, yy)
10998           {
10999             if (Feld[xx][yy] == element)
11000               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11001                                  CE_SCORE_GETS_ZERO);
11002           }
11003         }
11004       }
11005 #endif
11006
11007       break;
11008     }
11009
11010     case CA_SET_CE_ARTWORK:
11011     {
11012       int artwork_element = action_arg_element;
11013       boolean reset_frame = FALSE;
11014       int xx, yy;
11015
11016       if (action_arg == CA_ARG_ELEMENT_RESET)
11017         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11018                            element);
11019
11020       if (ei->gfx_element != artwork_element)
11021         reset_frame = TRUE;
11022
11023       ei->gfx_element = artwork_element;
11024
11025       SCAN_PLAYFIELD(xx, yy)
11026       {
11027         if (Feld[xx][yy] == element)
11028         {
11029           if (reset_frame)
11030           {
11031             ResetGfxAnimation(xx, yy);
11032             ResetRandomAnimationValue(xx, yy);
11033           }
11034
11035           TEST_DrawLevelField(xx, yy);
11036         }
11037       }
11038
11039       break;
11040     }
11041
11042     /* ---------- engine actions  ------------------------------------------ */
11043
11044     case CA_SET_ENGINE_SCAN_MODE:
11045     {
11046       InitPlayfieldScanMode(action_arg);
11047
11048       break;
11049     }
11050
11051     default:
11052       break;
11053   }
11054 }
11055
11056 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11057 {
11058   int old_element = Feld[x][y];
11059   int new_element = GetElementFromGroupElement(element);
11060   int previous_move_direction = MovDir[x][y];
11061 #if USE_NEW_CUSTOM_VALUE
11062   int last_ce_value = CustomValue[x][y];
11063 #endif
11064   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11065   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11066   boolean add_player_onto_element = (new_element_is_player &&
11067 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11068                                      /* this breaks SnakeBite when a snake is
11069                                         halfway through a door that closes */
11070                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11071                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11072 #endif
11073                                      IS_WALKABLE(old_element));
11074
11075 #if 0
11076   /* check if element under the player changes from accessible to unaccessible
11077      (needed for special case of dropping element which then changes) */
11078   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11079       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11080   {
11081     Bang(x, y);
11082
11083     return;
11084   }
11085 #endif
11086
11087   if (!add_player_onto_element)
11088   {
11089     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11090       RemoveMovingField(x, y);
11091     else
11092       RemoveField(x, y);
11093
11094     Feld[x][y] = new_element;
11095
11096 #if !USE_GFX_RESET_GFX_ANIMATION
11097     ResetGfxAnimation(x, y);
11098     ResetRandomAnimationValue(x, y);
11099 #endif
11100
11101     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11102       MovDir[x][y] = previous_move_direction;
11103
11104 #if USE_NEW_CUSTOM_VALUE
11105     if (element_info[new_element].use_last_ce_value)
11106       CustomValue[x][y] = last_ce_value;
11107 #endif
11108
11109     InitField_WithBug1(x, y, FALSE);
11110
11111     new_element = Feld[x][y];   /* element may have changed */
11112
11113 #if USE_GFX_RESET_GFX_ANIMATION
11114     ResetGfxAnimation(x, y);
11115     ResetRandomAnimationValue(x, y);
11116 #endif
11117
11118     TEST_DrawLevelField(x, y);
11119
11120     if (GFX_CRUMBLED(new_element))
11121       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11122   }
11123
11124 #if 1
11125   /* check if element under the player changes from accessible to unaccessible
11126      (needed for special case of dropping element which then changes) */
11127   /* (must be checked after creating new element for walkable group elements) */
11128 #if USE_FIX_KILLED_BY_NON_WALKABLE
11129   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11130       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11131   {
11132     Bang(x, y);
11133
11134     return;
11135   }
11136 #else
11137   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11138       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11139   {
11140     Bang(x, y);
11141
11142     return;
11143   }
11144 #endif
11145 #endif
11146
11147   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11148   if (new_element_is_player)
11149     RelocatePlayer(x, y, new_element);
11150
11151   if (is_change)
11152     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11153
11154   TestIfBadThingTouchesPlayer(x, y);
11155   TestIfPlayerTouchesCustomElement(x, y);
11156   TestIfElementTouchesCustomElement(x, y);
11157 }
11158
11159 static void CreateField(int x, int y, int element)
11160 {
11161   CreateFieldExt(x, y, element, FALSE);
11162 }
11163
11164 static void CreateElementFromChange(int x, int y, int element)
11165 {
11166   element = GET_VALID_RUNTIME_ELEMENT(element);
11167
11168 #if USE_STOP_CHANGED_ELEMENTS
11169   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11170   {
11171     int old_element = Feld[x][y];
11172
11173     /* prevent changed element from moving in same engine frame
11174        unless both old and new element can either fall or move */
11175     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11176         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11177       Stop[x][y] = TRUE;
11178   }
11179 #endif
11180
11181   CreateFieldExt(x, y, element, TRUE);
11182 }
11183
11184 static boolean ChangeElement(int x, int y, int element, int page)
11185 {
11186   struct ElementInfo *ei = &element_info[element];
11187   struct ElementChangeInfo *change = &ei->change_page[page];
11188   int ce_value = CustomValue[x][y];
11189   int ce_score = ei->collect_score;
11190   int target_element;
11191   int old_element = Feld[x][y];
11192
11193   /* always use default change event to prevent running into a loop */
11194   if (ChangeEvent[x][y] == -1)
11195     ChangeEvent[x][y] = CE_DELAY;
11196
11197   if (ChangeEvent[x][y] == CE_DELAY)
11198   {
11199     /* reset actual trigger element, trigger player and action element */
11200     change->actual_trigger_element = EL_EMPTY;
11201     change->actual_trigger_player = EL_EMPTY;
11202     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11203     change->actual_trigger_side = CH_SIDE_NONE;
11204     change->actual_trigger_ce_value = 0;
11205     change->actual_trigger_ce_score = 0;
11206   }
11207
11208   /* do not change elements more than a specified maximum number of changes */
11209   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11210     return FALSE;
11211
11212   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11213
11214   if (change->explode)
11215   {
11216     Bang(x, y);
11217
11218     return TRUE;
11219   }
11220
11221   if (change->use_target_content)
11222   {
11223     boolean complete_replace = TRUE;
11224     boolean can_replace[3][3];
11225     int xx, yy;
11226
11227     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11228     {
11229       boolean is_empty;
11230       boolean is_walkable;
11231       boolean is_diggable;
11232       boolean is_collectible;
11233       boolean is_removable;
11234       boolean is_destructible;
11235       int ex = x + xx - 1;
11236       int ey = y + yy - 1;
11237       int content_element = change->target_content.e[xx][yy];
11238       int e;
11239
11240       can_replace[xx][yy] = TRUE;
11241
11242       if (ex == x && ey == y)   /* do not check changing element itself */
11243         continue;
11244
11245       if (content_element == EL_EMPTY_SPACE)
11246       {
11247         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11248
11249         continue;
11250       }
11251
11252       if (!IN_LEV_FIELD(ex, ey))
11253       {
11254         can_replace[xx][yy] = FALSE;
11255         complete_replace = FALSE;
11256
11257         continue;
11258       }
11259
11260       e = Feld[ex][ey];
11261
11262       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11263         e = MovingOrBlocked2Element(ex, ey);
11264
11265       is_empty = (IS_FREE(ex, ey) ||
11266                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11267
11268       is_walkable     = (is_empty || IS_WALKABLE(e));
11269       is_diggable     = (is_empty || IS_DIGGABLE(e));
11270       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11271       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11272       is_removable    = (is_diggable || is_collectible);
11273
11274       can_replace[xx][yy] =
11275         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11276           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11277           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11278           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11279           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11280           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11281          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11282
11283       if (!can_replace[xx][yy])
11284         complete_replace = FALSE;
11285     }
11286
11287     if (!change->only_if_complete || complete_replace)
11288     {
11289       boolean something_has_changed = FALSE;
11290
11291       if (change->only_if_complete && change->use_random_replace &&
11292           RND(100) < change->random_percentage)
11293         return FALSE;
11294
11295       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11296       {
11297         int ex = x + xx - 1;
11298         int ey = y + yy - 1;
11299         int content_element;
11300
11301         if (can_replace[xx][yy] && (!change->use_random_replace ||
11302                                     RND(100) < change->random_percentage))
11303         {
11304           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11305             RemoveMovingField(ex, ey);
11306
11307           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11308
11309           content_element = change->target_content.e[xx][yy];
11310           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11311                                               ce_value, ce_score);
11312
11313           CreateElementFromChange(ex, ey, target_element);
11314
11315           something_has_changed = TRUE;
11316
11317           /* for symmetry reasons, freeze newly created border elements */
11318           if (ex != x || ey != y)
11319             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11320         }
11321       }
11322
11323       if (something_has_changed)
11324       {
11325         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11326         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11327       }
11328     }
11329   }
11330   else
11331   {
11332     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11333                                         ce_value, ce_score);
11334
11335     if (element == EL_DIAGONAL_GROWING ||
11336         element == EL_DIAGONAL_SHRINKING)
11337     {
11338       target_element = Store[x][y];
11339
11340       Store[x][y] = EL_EMPTY;
11341     }
11342
11343     CreateElementFromChange(x, y, target_element);
11344
11345     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11346     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11347   }
11348
11349   /* this uses direct change before indirect change */
11350   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11351
11352   return TRUE;
11353 }
11354
11355 #if USE_NEW_DELAYED_ACTION
11356
11357 static void HandleElementChange(int x, int y, int page)
11358 {
11359   int element = MovingOrBlocked2Element(x, y);
11360   struct ElementInfo *ei = &element_info[element];
11361   struct ElementChangeInfo *change = &ei->change_page[page];
11362   boolean handle_action_before_change = FALSE;
11363
11364 #ifdef DEBUG
11365   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11366       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11367   {
11368     printf("\n\n");
11369     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11370            x, y, element, element_info[element].token_name);
11371     printf("HandleElementChange(): This should never happen!\n");
11372     printf("\n\n");
11373   }
11374 #endif
11375
11376   /* this can happen with classic bombs on walkable, changing elements */
11377   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11378   {
11379 #if 0
11380     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11381       ChangeDelay[x][y] = 0;
11382 #endif
11383
11384     return;
11385   }
11386
11387   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11388   {
11389     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11390
11391     if (change->can_change)
11392     {
11393 #if 1
11394       /* !!! not clear why graphic animation should be reset at all here !!! */
11395       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11396 #if USE_GFX_RESET_WHEN_NOT_MOVING
11397       /* when a custom element is about to change (for example by change delay),
11398          do not reset graphic animation when the custom element is moving */
11399       if (!IS_MOVING(x, y))
11400 #endif
11401       {
11402         ResetGfxAnimation(x, y);
11403         ResetRandomAnimationValue(x, y);
11404       }
11405 #endif
11406
11407       if (change->pre_change_function)
11408         change->pre_change_function(x, y);
11409     }
11410   }
11411
11412   ChangeDelay[x][y]--;
11413
11414   if (ChangeDelay[x][y] != 0)           /* continue element change */
11415   {
11416     if (change->can_change)
11417     {
11418       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11419
11420       if (IS_ANIMATED(graphic))
11421         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11422
11423       if (change->change_function)
11424         change->change_function(x, y);
11425     }
11426   }
11427   else                                  /* finish element change */
11428   {
11429     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11430     {
11431       page = ChangePage[x][y];
11432       ChangePage[x][y] = -1;
11433
11434       change = &ei->change_page[page];
11435     }
11436
11437     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11438     {
11439       ChangeDelay[x][y] = 1;            /* try change after next move step */
11440       ChangePage[x][y] = page;          /* remember page to use for change */
11441
11442       return;
11443     }
11444
11445 #if 1
11446     /* special case: set new level random seed before changing element */
11447     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11448       handle_action_before_change = TRUE;
11449
11450     if (change->has_action && handle_action_before_change)
11451       ExecuteCustomElementAction(x, y, element, page);
11452 #endif
11453
11454     if (change->can_change)
11455     {
11456       if (ChangeElement(x, y, element, page))
11457       {
11458         if (change->post_change_function)
11459           change->post_change_function(x, y);
11460       }
11461     }
11462
11463     if (change->has_action && !handle_action_before_change)
11464       ExecuteCustomElementAction(x, y, element, page);
11465   }
11466 }
11467
11468 #else
11469
11470 static void HandleElementChange(int x, int y, int page)
11471 {
11472   int element = MovingOrBlocked2Element(x, y);
11473   struct ElementInfo *ei = &element_info[element];
11474   struct ElementChangeInfo *change = &ei->change_page[page];
11475
11476 #ifdef DEBUG
11477   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11478   {
11479     printf("\n\n");
11480     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11481            x, y, element, element_info[element].token_name);
11482     printf("HandleElementChange(): This should never happen!\n");
11483     printf("\n\n");
11484   }
11485 #endif
11486
11487   /* this can happen with classic bombs on walkable, changing elements */
11488   if (!CAN_CHANGE(element))
11489   {
11490 #if 0
11491     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11492       ChangeDelay[x][y] = 0;
11493 #endif
11494
11495     return;
11496   }
11497
11498   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11499   {
11500     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11501
11502     ResetGfxAnimation(x, y);
11503     ResetRandomAnimationValue(x, y);
11504
11505     if (change->pre_change_function)
11506       change->pre_change_function(x, y);
11507   }
11508
11509   ChangeDelay[x][y]--;
11510
11511   if (ChangeDelay[x][y] != 0)           /* continue element change */
11512   {
11513     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11514
11515     if (IS_ANIMATED(graphic))
11516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517
11518     if (change->change_function)
11519       change->change_function(x, y);
11520   }
11521   else                                  /* finish element change */
11522   {
11523     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11524     {
11525       page = ChangePage[x][y];
11526       ChangePage[x][y] = -1;
11527
11528       change = &ei->change_page[page];
11529     }
11530
11531     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11532     {
11533       ChangeDelay[x][y] = 1;            /* try change after next move step */
11534       ChangePage[x][y] = page;          /* remember page to use for change */
11535
11536       return;
11537     }
11538
11539     if (ChangeElement(x, y, element, page))
11540     {
11541       if (change->post_change_function)
11542         change->post_change_function(x, y);
11543     }
11544   }
11545 }
11546
11547 #endif
11548
11549 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11550                                               int trigger_element,
11551                                               int trigger_event,
11552                                               int trigger_player,
11553                                               int trigger_side,
11554                                               int trigger_page)
11555 {
11556   boolean change_done_any = FALSE;
11557   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11558   int i;
11559
11560   if (!(trigger_events[trigger_element][trigger_event]))
11561     return FALSE;
11562
11563 #if 0
11564   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11565          trigger_event, recursion_loop_depth, recursion_loop_detected,
11566          recursion_loop_element, EL_NAME(recursion_loop_element));
11567 #endif
11568
11569   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11570
11571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11572   {
11573     int element = EL_CUSTOM_START + i;
11574     boolean change_done = FALSE;
11575     int p;
11576
11577     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11578         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11579       continue;
11580
11581     for (p = 0; p < element_info[element].num_change_pages; p++)
11582     {
11583       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11584
11585       if (change->can_change_or_has_action &&
11586           change->has_event[trigger_event] &&
11587           change->trigger_side & trigger_side &&
11588           change->trigger_player & trigger_player &&
11589           change->trigger_page & trigger_page_bits &&
11590           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11591       {
11592         change->actual_trigger_element = trigger_element;
11593         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11594         change->actual_trigger_player_bits = trigger_player;
11595         change->actual_trigger_side = trigger_side;
11596         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11597         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11598
11599 #if 0
11600         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11601                element, EL_NAME(element), p);
11602 #endif
11603
11604         if ((change->can_change && !change_done) || change->has_action)
11605         {
11606           int x, y;
11607
11608           SCAN_PLAYFIELD(x, y)
11609           {
11610             if (Feld[x][y] == element)
11611             {
11612               if (change->can_change && !change_done)
11613               {
11614 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11615                 /* if element already changed in this frame, not only prevent
11616                    another element change (checked in ChangeElement()), but
11617                    also prevent additional element actions for this element */
11618
11619                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11620                     !level.use_action_after_change_bug)
11621                   continue;
11622 #endif
11623
11624 #if 0
11625                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11626                        element, EL_NAME(element), p);
11627 #endif
11628
11629                 ChangeDelay[x][y] = 1;
11630                 ChangeEvent[x][y] = trigger_event;
11631
11632                 HandleElementChange(x, y, p);
11633               }
11634 #if USE_NEW_DELAYED_ACTION
11635               else if (change->has_action)
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
11648 #if 0
11649                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11650                        element, EL_NAME(element), p);
11651 #endif
11652
11653                 ExecuteCustomElementAction(x, y, element, p);
11654                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11655               }
11656 #else
11657               if (change->has_action)
11658               {
11659                 ExecuteCustomElementAction(x, y, element, p);
11660                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661               }
11662 #endif
11663             }
11664           }
11665
11666           if (change->can_change)
11667           {
11668             change_done = TRUE;
11669             change_done_any = TRUE;
11670
11671 #if 0
11672             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11673                    element, EL_NAME(element), p);
11674 #endif
11675
11676           }
11677         }
11678       }
11679     }
11680   }
11681
11682   RECURSION_LOOP_DETECTION_END();
11683
11684   return change_done_any;
11685 }
11686
11687 static boolean CheckElementChangeExt(int x, int y,
11688                                      int element,
11689                                      int trigger_element,
11690                                      int trigger_event,
11691                                      int trigger_player,
11692                                      int trigger_side)
11693 {
11694   boolean change_done = FALSE;
11695   int p;
11696
11697   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11698       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11699     return FALSE;
11700
11701   if (Feld[x][y] == EL_BLOCKED)
11702   {
11703     Blocked2Moving(x, y, &x, &y);
11704     element = Feld[x][y];
11705   }
11706
11707 #if 0
11708   /* check if element has already changed */
11709   if (Feld[x][y] != element)
11710     return FALSE;
11711 #else
11712   /* check if element has already changed or is about to change after moving */
11713   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11714        Feld[x][y] != element) ||
11715
11716       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11717        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11718         ChangePage[x][y] != -1)))
11719     return FALSE;
11720 #endif
11721
11722 #if 0
11723   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11724          trigger_event, recursion_loop_depth, recursion_loop_detected,
11725          recursion_loop_element, EL_NAME(recursion_loop_element));
11726 #endif
11727
11728   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11729
11730 #if 0
11731   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11732 #endif
11733
11734   for (p = 0; p < element_info[element].num_change_pages; p++)
11735   {
11736     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11737
11738     /* check trigger element for all events where the element that is checked
11739        for changing interacts with a directly adjacent element -- this is
11740        different to element changes that affect other elements to change on the
11741        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11742     boolean check_trigger_element =
11743       (trigger_event == CE_TOUCHING_X ||
11744        trigger_event == CE_HITTING_X ||
11745        trigger_event == CE_HIT_BY_X ||
11746 #if 1
11747        /* this one was forgotten until 3.2.3 */
11748        trigger_event == CE_DIGGING_X);
11749 #endif
11750
11751     if (change->can_change_or_has_action &&
11752         change->has_event[trigger_event] &&
11753         change->trigger_side & trigger_side &&
11754         change->trigger_player & trigger_player &&
11755         (!check_trigger_element ||
11756          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11757     {
11758       change->actual_trigger_element = trigger_element;
11759       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11760       change->actual_trigger_player_bits = trigger_player;
11761       change->actual_trigger_side = trigger_side;
11762       change->actual_trigger_ce_value = CustomValue[x][y];
11763       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11764
11765       /* special case: trigger element not at (x,y) position for some events */
11766       if (check_trigger_element)
11767       {
11768         static struct
11769         {
11770           int dx, dy;
11771         } move_xy[] =
11772           {
11773             {  0,  0 },
11774             { -1,  0 },
11775             { +1,  0 },
11776             {  0,  0 },
11777             {  0, -1 },
11778             {  0,  0 }, { 0, 0 }, { 0, 0 },
11779             {  0, +1 }
11780           };
11781
11782         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11783         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11784
11785         change->actual_trigger_ce_value = CustomValue[xx][yy];
11786         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11787       }
11788
11789       if (change->can_change && !change_done)
11790       {
11791         ChangeDelay[x][y] = 1;
11792         ChangeEvent[x][y] = trigger_event;
11793
11794         HandleElementChange(x, y, p);
11795
11796         change_done = TRUE;
11797       }
11798 #if USE_NEW_DELAYED_ACTION
11799       else if (change->has_action)
11800       {
11801         ExecuteCustomElementAction(x, y, element, p);
11802         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11803       }
11804 #else
11805       if (change->has_action)
11806       {
11807         ExecuteCustomElementAction(x, y, element, p);
11808         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11809       }
11810 #endif
11811     }
11812   }
11813
11814   RECURSION_LOOP_DETECTION_END();
11815
11816   return change_done;
11817 }
11818
11819 static void PlayPlayerSound(struct PlayerInfo *player)
11820 {
11821   int jx = player->jx, jy = player->jy;
11822   int sound_element = player->artwork_element;
11823   int last_action = player->last_action_waiting;
11824   int action = player->action_waiting;
11825
11826   if (player->is_waiting)
11827   {
11828     if (action != last_action)
11829       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11830     else
11831       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11832   }
11833   else
11834   {
11835     if (action != last_action)
11836       StopSound(element_info[sound_element].sound[last_action]);
11837
11838     if (last_action == ACTION_SLEEPING)
11839       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11840   }
11841 }
11842
11843 static void PlayAllPlayersSound()
11844 {
11845   int i;
11846
11847   for (i = 0; i < MAX_PLAYERS; i++)
11848     if (stored_player[i].active)
11849       PlayPlayerSound(&stored_player[i]);
11850 }
11851
11852 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11853 {
11854   boolean last_waiting = player->is_waiting;
11855   int move_dir = player->MovDir;
11856
11857   player->dir_waiting = move_dir;
11858   player->last_action_waiting = player->action_waiting;
11859
11860   if (is_waiting)
11861   {
11862     if (!last_waiting)          /* not waiting -> waiting */
11863     {
11864       player->is_waiting = TRUE;
11865
11866       player->frame_counter_bored =
11867         FrameCounter +
11868         game.player_boring_delay_fixed +
11869         GetSimpleRandom(game.player_boring_delay_random);
11870       player->frame_counter_sleeping =
11871         FrameCounter +
11872         game.player_sleeping_delay_fixed +
11873         GetSimpleRandom(game.player_sleeping_delay_random);
11874
11875       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11876     }
11877
11878     if (game.player_sleeping_delay_fixed +
11879         game.player_sleeping_delay_random > 0 &&
11880         player->anim_delay_counter == 0 &&
11881         player->post_delay_counter == 0 &&
11882         FrameCounter >= player->frame_counter_sleeping)
11883       player->is_sleeping = TRUE;
11884     else if (game.player_boring_delay_fixed +
11885              game.player_boring_delay_random > 0 &&
11886              FrameCounter >= player->frame_counter_bored)
11887       player->is_bored = TRUE;
11888
11889     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11890                               player->is_bored ? ACTION_BORING :
11891                               ACTION_WAITING);
11892
11893     if (player->is_sleeping && player->use_murphy)
11894     {
11895       /* special case for sleeping Murphy when leaning against non-free tile */
11896
11897       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11898           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11899            !IS_MOVING(player->jx - 1, player->jy)))
11900         move_dir = MV_LEFT;
11901       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11902                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11903                 !IS_MOVING(player->jx + 1, player->jy)))
11904         move_dir = MV_RIGHT;
11905       else
11906         player->is_sleeping = FALSE;
11907
11908       player->dir_waiting = move_dir;
11909     }
11910
11911     if (player->is_sleeping)
11912     {
11913       if (player->num_special_action_sleeping > 0)
11914       {
11915         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11916         {
11917           int last_special_action = player->special_action_sleeping;
11918           int num_special_action = player->num_special_action_sleeping;
11919           int special_action =
11920             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11921              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11922              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11923              last_special_action + 1 : ACTION_SLEEPING);
11924           int special_graphic =
11925             el_act_dir2img(player->artwork_element, special_action, move_dir);
11926
11927           player->anim_delay_counter =
11928             graphic_info[special_graphic].anim_delay_fixed +
11929             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11930           player->post_delay_counter =
11931             graphic_info[special_graphic].post_delay_fixed +
11932             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11933
11934           player->special_action_sleeping = special_action;
11935         }
11936
11937         if (player->anim_delay_counter > 0)
11938         {
11939           player->action_waiting = player->special_action_sleeping;
11940           player->anim_delay_counter--;
11941         }
11942         else if (player->post_delay_counter > 0)
11943         {
11944           player->post_delay_counter--;
11945         }
11946       }
11947     }
11948     else if (player->is_bored)
11949     {
11950       if (player->num_special_action_bored > 0)
11951       {
11952         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11953         {
11954           int special_action =
11955             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11956           int special_graphic =
11957             el_act_dir2img(player->artwork_element, special_action, move_dir);
11958
11959           player->anim_delay_counter =
11960             graphic_info[special_graphic].anim_delay_fixed +
11961             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11962           player->post_delay_counter =
11963             graphic_info[special_graphic].post_delay_fixed +
11964             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11965
11966           player->special_action_bored = special_action;
11967         }
11968
11969         if (player->anim_delay_counter > 0)
11970         {
11971           player->action_waiting = player->special_action_bored;
11972           player->anim_delay_counter--;
11973         }
11974         else if (player->post_delay_counter > 0)
11975         {
11976           player->post_delay_counter--;
11977         }
11978       }
11979     }
11980   }
11981   else if (last_waiting)        /* waiting -> not waiting */
11982   {
11983     player->is_waiting = FALSE;
11984     player->is_bored = FALSE;
11985     player->is_sleeping = FALSE;
11986
11987     player->frame_counter_bored = -1;
11988     player->frame_counter_sleeping = -1;
11989
11990     player->anim_delay_counter = 0;
11991     player->post_delay_counter = 0;
11992
11993     player->dir_waiting = player->MovDir;
11994     player->action_waiting = ACTION_DEFAULT;
11995
11996     player->special_action_bored = ACTION_DEFAULT;
11997     player->special_action_sleeping = ACTION_DEFAULT;
11998   }
11999 }
12000
12001 static void CheckSingleStepMode(struct PlayerInfo *player)
12002 {
12003   if (tape.single_step && tape.recording && !tape.pausing)
12004   {
12005     /* as it is called "single step mode", just return to pause mode when the
12006        player stopped moving after one tile (or never starts moving at all) */
12007     if (!player->is_moving && !player->is_pushing)
12008     {
12009       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12010       SnapField(player, 0, 0);                  /* stop snapping */
12011     }
12012   }
12013 }
12014
12015 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12016 {
12017   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12018   int left      = player_action & JOY_LEFT;
12019   int right     = player_action & JOY_RIGHT;
12020   int up        = player_action & JOY_UP;
12021   int down      = player_action & JOY_DOWN;
12022   int button1   = player_action & JOY_BUTTON_1;
12023   int button2   = player_action & JOY_BUTTON_2;
12024   int dx        = (left ? -1 : right ? 1 : 0);
12025   int dy        = (up   ? -1 : down  ? 1 : 0);
12026
12027   if (!player->active || tape.pausing)
12028     return 0;
12029
12030   if (player_action)
12031   {
12032     if (button1)
12033       snapped = SnapField(player, dx, dy);
12034     else
12035     {
12036       if (button2)
12037         dropped = DropElement(player);
12038
12039       moved = MovePlayer(player, dx, dy);
12040     }
12041
12042     CheckSingleStepMode(player);
12043
12044     SetPlayerWaiting(player, FALSE);
12045
12046     return player_action;
12047   }
12048   else
12049   {
12050     /* no actions for this player (no input at player's configured device) */
12051
12052     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12053     SnapField(player, 0, 0);
12054     CheckGravityMovementWhenNotMoving(player);
12055
12056     if (player->MovPos == 0)
12057       SetPlayerWaiting(player, TRUE);
12058
12059     if (player->MovPos == 0)    /* needed for tape.playing */
12060       player->is_moving = FALSE;
12061
12062     player->is_dropping = FALSE;
12063     player->is_dropping_pressed = FALSE;
12064     player->drop_pressed_delay = 0;
12065
12066     CheckSingleStepMode(player);
12067
12068     return 0;
12069   }
12070 }
12071
12072 static void CheckLevelTime()
12073 {
12074   int i;
12075
12076   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12077   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12078   {
12079     if (level.native_em_level->lev->home == 0)  /* all players at home */
12080     {
12081       PlayerWins(local_player);
12082
12083       AllPlayersGone = TRUE;
12084
12085       level.native_em_level->lev->home = -1;
12086     }
12087
12088     if (level.native_em_level->ply[0]->alive == 0 &&
12089         level.native_em_level->ply[1]->alive == 0 &&
12090         level.native_em_level->ply[2]->alive == 0 &&
12091         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12092       AllPlayersGone = TRUE;
12093   }
12094   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12095   {
12096     if (game_sp.LevelSolved &&
12097         !game_sp.GameOver)                              /* game won */
12098     {
12099       PlayerWins(local_player);
12100
12101       game_sp.GameOver = TRUE;
12102
12103       AllPlayersGone = TRUE;
12104     }
12105
12106     if (game_sp.GameOver)                               /* game lost */
12107       AllPlayersGone = TRUE;
12108   }
12109
12110   if (TimeFrames >= FRAMES_PER_SECOND)
12111   {
12112     TimeFrames = 0;
12113     TapeTime++;
12114
12115     for (i = 0; i < MAX_PLAYERS; i++)
12116     {
12117       struct PlayerInfo *player = &stored_player[i];
12118
12119       if (SHIELD_ON(player))
12120       {
12121         player->shield_normal_time_left--;
12122
12123         if (player->shield_deadly_time_left > 0)
12124           player->shield_deadly_time_left--;
12125       }
12126     }
12127
12128     if (!local_player->LevelSolved && !level.use_step_counter)
12129     {
12130       TimePlayed++;
12131
12132       if (TimeLeft > 0)
12133       {
12134         TimeLeft--;
12135
12136         if (TimeLeft <= 10 && setup.time_limit)
12137           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12138
12139 #if 1
12140         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12141
12142         DisplayGameControlValues();
12143 #else
12144         DrawGameValue_Time(TimeLeft);
12145 #endif
12146
12147         if (!TimeLeft && setup.time_limit)
12148         {
12149           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150             level.native_em_level->lev->killed_out_of_time = TRUE;
12151           else
12152             for (i = 0; i < MAX_PLAYERS; i++)
12153               KillPlayer(&stored_player[i]);
12154         }
12155       }
12156 #if 1
12157       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12158       {
12159         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12160
12161         DisplayGameControlValues();
12162       }
12163 #else
12164       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12165         DrawGameValue_Time(TimePlayed);
12166 #endif
12167
12168       level.native_em_level->lev->time =
12169         (level.time == 0 ? TimePlayed : TimeLeft);
12170     }
12171
12172     if (tape.recording || tape.playing)
12173       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12174   }
12175
12176 #if 1
12177   UpdateAndDisplayGameControlValues();
12178 #else
12179   UpdateGameDoorValues();
12180   DrawGameDoorValues();
12181 #endif
12182 }
12183
12184 void AdvanceFrameAndPlayerCounters(int player_nr)
12185 {
12186   int i;
12187
12188   /* advance frame counters (global frame counter and time frame counter) */
12189   FrameCounter++;
12190   TimeFrames++;
12191
12192   /* advance player counters (counters for move delay, move animation etc.) */
12193   for (i = 0; i < MAX_PLAYERS; i++)
12194   {
12195     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12196     int move_delay_value = stored_player[i].move_delay_value;
12197     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12198
12199     if (!advance_player_counters)       /* not all players may be affected */
12200       continue;
12201
12202 #if USE_NEW_PLAYER_ANIM
12203     if (move_frames == 0)       /* less than one move per game frame */
12204     {
12205       int stepsize = TILEX / move_delay_value;
12206       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12207       int count = (stored_player[i].is_moving ?
12208                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12209
12210       if (count % delay == 0)
12211         move_frames = 1;
12212     }
12213 #endif
12214
12215     stored_player[i].Frame += move_frames;
12216
12217     if (stored_player[i].MovPos != 0)
12218       stored_player[i].StepFrame += move_frames;
12219
12220     if (stored_player[i].move_delay > 0)
12221       stored_player[i].move_delay--;
12222
12223     /* due to bugs in previous versions, counter must count up, not down */
12224     if (stored_player[i].push_delay != -1)
12225       stored_player[i].push_delay++;
12226
12227     if (stored_player[i].drop_delay > 0)
12228       stored_player[i].drop_delay--;
12229
12230     if (stored_player[i].is_dropping_pressed)
12231       stored_player[i].drop_pressed_delay++;
12232   }
12233 }
12234
12235 void StartGameActions(boolean init_network_game, boolean record_tape,
12236                       long random_seed)
12237 {
12238   unsigned long new_random_seed = InitRND(random_seed);
12239
12240   if (record_tape)
12241     TapeStartRecording(new_random_seed);
12242
12243 #if defined(NETWORK_AVALIABLE)
12244   if (init_network_game)
12245   {
12246     SendToServer_StartPlaying();
12247
12248     return;
12249   }
12250 #endif
12251
12252   InitGame();
12253 }
12254
12255 void GameActions()
12256 {
12257   static unsigned long game_frame_delay = 0;
12258   unsigned long game_frame_delay_value;
12259   byte *recorded_player_action;
12260   byte summarized_player_action = 0;
12261   byte tape_action[MAX_PLAYERS];
12262   int i;
12263
12264   /* detect endless loops, caused by custom element programming */
12265   if (recursion_loop_detected && recursion_loop_depth == 0)
12266   {
12267     char *message = getStringCat3("Internal Error ! Element ",
12268                                   EL_NAME(recursion_loop_element),
12269                                   " caused endless loop ! Quit the game ?");
12270
12271     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12272           EL_NAME(recursion_loop_element));
12273
12274     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12275
12276     recursion_loop_detected = FALSE;    /* if game should be continued */
12277
12278     free(message);
12279
12280     return;
12281   }
12282
12283   if (game.restart_level)
12284     StartGameActions(options.network, setup.autorecord, level.random_seed);
12285
12286   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12287   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12288   {
12289     if (level.native_em_level->lev->home == 0)  /* all players at home */
12290     {
12291       PlayerWins(local_player);
12292
12293       AllPlayersGone = TRUE;
12294
12295       level.native_em_level->lev->home = -1;
12296     }
12297
12298     if (level.native_em_level->ply[0]->alive == 0 &&
12299         level.native_em_level->ply[1]->alive == 0 &&
12300         level.native_em_level->ply[2]->alive == 0 &&
12301         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12302       AllPlayersGone = TRUE;
12303   }
12304   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12305   {
12306     if (game_sp.LevelSolved &&
12307         !game_sp.GameOver)                              /* game won */
12308     {
12309       PlayerWins(local_player);
12310
12311       game_sp.GameOver = TRUE;
12312
12313       AllPlayersGone = TRUE;
12314     }
12315
12316     if (game_sp.GameOver)                               /* game lost */
12317       AllPlayersGone = TRUE;
12318   }
12319
12320   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12321     GameWon();
12322
12323   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12324     TapeStop();
12325
12326   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12327     return;
12328
12329   game_frame_delay_value =
12330     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12331
12332   if (tape.playing && tape.warp_forward && !tape.pausing)
12333     game_frame_delay_value = 0;
12334
12335   /* ---------- main game synchronization point ---------- */
12336
12337   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12338
12339   if (network_playing && !network_player_action_received)
12340   {
12341     /* try to get network player actions in time */
12342
12343 #if defined(NETWORK_AVALIABLE)
12344     /* last chance to get network player actions without main loop delay */
12345     HandleNetworking();
12346 #endif
12347
12348     /* game was quit by network peer */
12349     if (game_status != GAME_MODE_PLAYING)
12350       return;
12351
12352     if (!network_player_action_received)
12353       return;           /* failed to get network player actions in time */
12354
12355     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12356   }
12357
12358   if (tape.pausing)
12359     return;
12360
12361   /* at this point we know that we really continue executing the game */
12362
12363   network_player_action_received = FALSE;
12364
12365   /* when playing tape, read previously recorded player input from tape data */
12366   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12367
12368 #if 1
12369   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12370   if (tape.pausing)
12371     return;
12372 #endif
12373
12374   if (tape.set_centered_player)
12375   {
12376     game.centered_player_nr_next = tape.centered_player_nr_next;
12377     game.set_centered_player = TRUE;
12378   }
12379
12380   for (i = 0; i < MAX_PLAYERS; i++)
12381   {
12382     summarized_player_action |= stored_player[i].action;
12383
12384     if (!network_playing)
12385       stored_player[i].effective_action = stored_player[i].action;
12386   }
12387
12388 #if defined(NETWORK_AVALIABLE)
12389   if (network_playing)
12390     SendToServer_MovePlayer(summarized_player_action);
12391 #endif
12392
12393   if (!options.network && !setup.team_mode)
12394     local_player->effective_action = summarized_player_action;
12395
12396   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12397   {
12398     for (i = 0; i < MAX_PLAYERS; i++)
12399       stored_player[i].effective_action =
12400         (i == game.centered_player_nr ? summarized_player_action : 0);
12401   }
12402
12403   if (recorded_player_action != NULL)
12404     for (i = 0; i < MAX_PLAYERS; i++)
12405       stored_player[i].effective_action = recorded_player_action[i];
12406
12407   for (i = 0; i < MAX_PLAYERS; i++)
12408   {
12409     tape_action[i] = stored_player[i].effective_action;
12410
12411     /* (this can only happen in the R'n'D game engine) */
12412     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12413       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12414   }
12415
12416   /* only record actions from input devices, but not programmed actions */
12417   if (tape.recording)
12418     TapeRecordAction(tape_action);
12419
12420 #if USE_NEW_PLAYER_ASSIGNMENTS
12421   {
12422     byte mapped_action[MAX_PLAYERS];
12423
12424     for (i = 0; i < MAX_PLAYERS; i++)
12425       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12426
12427     for (i = 0; i < MAX_PLAYERS; i++)
12428       stored_player[i].effective_action = mapped_action[i];
12429   }
12430 #endif
12431
12432   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12433   {
12434     GameActions_EM_Main();
12435   }
12436   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12437   {
12438     GameActions_SP_Main();
12439   }
12440   else
12441   {
12442     GameActions_RND();
12443   }
12444 }
12445
12446 void GameActions_EM_Main()
12447 {
12448   byte effective_action[MAX_PLAYERS];
12449   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12450   int i;
12451
12452   for (i = 0; i < MAX_PLAYERS; i++)
12453     effective_action[i] = stored_player[i].effective_action;
12454
12455   GameActions_EM(effective_action, warp_mode);
12456
12457   CheckLevelTime();
12458
12459   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12460 }
12461
12462 void GameActions_SP_Main()
12463 {
12464   byte effective_action[MAX_PLAYERS];
12465   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12466   int i;
12467
12468   for (i = 0; i < MAX_PLAYERS; i++)
12469     effective_action[i] = stored_player[i].effective_action;
12470
12471   GameActions_SP(effective_action, warp_mode);
12472
12473   CheckLevelTime();
12474
12475   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12476 }
12477
12478 void GameActions_RND()
12479 {
12480   int magic_wall_x = 0, magic_wall_y = 0;
12481   int i, x, y, element, graphic;
12482
12483   InitPlayfieldScanModeVars();
12484
12485 #if USE_ONE_MORE_CHANGE_PER_FRAME
12486   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12487   {
12488     SCAN_PLAYFIELD(x, y)
12489     {
12490       ChangeCount[x][y] = 0;
12491       ChangeEvent[x][y] = -1;
12492     }
12493   }
12494 #endif
12495
12496   if (game.set_centered_player)
12497   {
12498     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12499
12500     /* switching to "all players" only possible if all players fit to screen */
12501     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12502     {
12503       game.centered_player_nr_next = game.centered_player_nr;
12504       game.set_centered_player = FALSE;
12505     }
12506
12507     /* do not switch focus to non-existing (or non-active) player */
12508     if (game.centered_player_nr_next >= 0 &&
12509         !stored_player[game.centered_player_nr_next].active)
12510     {
12511       game.centered_player_nr_next = game.centered_player_nr;
12512       game.set_centered_player = FALSE;
12513     }
12514   }
12515
12516   if (game.set_centered_player &&
12517       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12518   {
12519     int sx, sy;
12520
12521     if (game.centered_player_nr_next == -1)
12522     {
12523       setScreenCenteredToAllPlayers(&sx, &sy);
12524     }
12525     else
12526     {
12527       sx = stored_player[game.centered_player_nr_next].jx;
12528       sy = stored_player[game.centered_player_nr_next].jy;
12529     }
12530
12531     game.centered_player_nr = game.centered_player_nr_next;
12532     game.set_centered_player = FALSE;
12533
12534     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12535     DrawGameDoorValues();
12536   }
12537
12538   for (i = 0; i < MAX_PLAYERS; i++)
12539   {
12540     int actual_player_action = stored_player[i].effective_action;
12541
12542 #if 1
12543     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12544        - rnd_equinox_tetrachloride 048
12545        - rnd_equinox_tetrachloride_ii 096
12546        - rnd_emanuel_schmieg 002
12547        - doctor_sloan_ww 001, 020
12548     */
12549     if (stored_player[i].MovPos == 0)
12550       CheckGravityMovement(&stored_player[i]);
12551 #endif
12552
12553     /* overwrite programmed action with tape action */
12554     if (stored_player[i].programmed_action)
12555       actual_player_action = stored_player[i].programmed_action;
12556
12557     PlayerActions(&stored_player[i], actual_player_action);
12558
12559     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12560   }
12561
12562   ScrollScreen(NULL, SCROLL_GO_ON);
12563
12564   /* for backwards compatibility, the following code emulates a fixed bug that
12565      occured when pushing elements (causing elements that just made their last
12566      pushing step to already (if possible) make their first falling step in the
12567      same game frame, which is bad); this code is also needed to use the famous
12568      "spring push bug" which is used in older levels and might be wanted to be
12569      used also in newer levels, but in this case the buggy pushing code is only
12570      affecting the "spring" element and no other elements */
12571
12572   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12573   {
12574     for (i = 0; i < MAX_PLAYERS; i++)
12575     {
12576       struct PlayerInfo *player = &stored_player[i];
12577       int x = player->jx;
12578       int y = player->jy;
12579
12580       if (player->active && player->is_pushing && player->is_moving &&
12581           IS_MOVING(x, y) &&
12582           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12583            Feld[x][y] == EL_SPRING))
12584       {
12585         ContinueMoving(x, y);
12586
12587         /* continue moving after pushing (this is actually a bug) */
12588         if (!IS_MOVING(x, y))
12589           Stop[x][y] = FALSE;
12590       }
12591     }
12592   }
12593
12594 #if 0
12595   debug_print_timestamp(0, "start main loop profiling");
12596 #endif
12597
12598   SCAN_PLAYFIELD(x, y)
12599   {
12600     ChangeCount[x][y] = 0;
12601     ChangeEvent[x][y] = -1;
12602
12603     /* this must be handled before main playfield loop */
12604     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12605     {
12606       MovDelay[x][y]--;
12607       if (MovDelay[x][y] <= 0)
12608         RemoveField(x, y);
12609     }
12610
12611 #if USE_NEW_SNAP_DELAY
12612     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12613     {
12614       MovDelay[x][y]--;
12615       if (MovDelay[x][y] <= 0)
12616       {
12617         RemoveField(x, y);
12618         TEST_DrawLevelField(x, y);
12619
12620         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12621       }
12622     }
12623 #endif
12624
12625 #if DEBUG
12626     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12627     {
12628       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12629       printf("GameActions(): This should never happen!\n");
12630
12631       ChangePage[x][y] = -1;
12632     }
12633 #endif
12634
12635     Stop[x][y] = FALSE;
12636     if (WasJustMoving[x][y] > 0)
12637       WasJustMoving[x][y]--;
12638     if (WasJustFalling[x][y] > 0)
12639       WasJustFalling[x][y]--;
12640     if (CheckCollision[x][y] > 0)
12641       CheckCollision[x][y]--;
12642     if (CheckImpact[x][y] > 0)
12643       CheckImpact[x][y]--;
12644
12645     GfxFrame[x][y]++;
12646
12647     /* reset finished pushing action (not done in ContinueMoving() to allow
12648        continuous pushing animation for elements with zero push delay) */
12649     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12650     {
12651       ResetGfxAnimation(x, y);
12652       TEST_DrawLevelField(x, y);
12653     }
12654
12655 #if DEBUG
12656     if (IS_BLOCKED(x, y))
12657     {
12658       int oldx, oldy;
12659
12660       Blocked2Moving(x, y, &oldx, &oldy);
12661       if (!IS_MOVING(oldx, oldy))
12662       {
12663         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12664         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12665         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12666         printf("GameActions(): This should never happen!\n");
12667       }
12668     }
12669 #endif
12670   }
12671
12672 #if 0
12673   debug_print_timestamp(0, "- time for pre-main loop:");
12674 #endif
12675
12676 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12677   SCAN_PLAYFIELD(x, y)
12678   {
12679     element = Feld[x][y];
12680     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12681
12682 #if 1
12683     {
12684 #if 1
12685       int element2 = element;
12686       int graphic2 = graphic;
12687 #else
12688       int element2 = Feld[x][y];
12689       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12690 #endif
12691       int last_gfx_frame = GfxFrame[x][y];
12692
12693       if (graphic_info[graphic2].anim_global_sync)
12694         GfxFrame[x][y] = FrameCounter;
12695       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12696         GfxFrame[x][y] = CustomValue[x][y];
12697       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12698         GfxFrame[x][y] = element_info[element2].collect_score;
12699       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12700         GfxFrame[x][y] = ChangeDelay[x][y];
12701
12702       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12703         DrawLevelGraphicAnimation(x, y, graphic2);
12704     }
12705 #else
12706     ResetGfxFrame(x, y, TRUE);
12707 #endif
12708
12709 #if 1
12710     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12711         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12712       ResetRandomAnimationValue(x, y);
12713 #endif
12714
12715 #if 1
12716     SetRandomAnimationValue(x, y);
12717 #endif
12718
12719 #if 1
12720     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12721 #endif
12722   }
12723 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12724
12725 #if 0
12726   debug_print_timestamp(0, "- time for TEST loop:     -->");
12727 #endif
12728
12729   SCAN_PLAYFIELD(x, y)
12730   {
12731     element = Feld[x][y];
12732     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12733
12734     ResetGfxFrame(x, y, TRUE);
12735
12736     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12737         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12738       ResetRandomAnimationValue(x, y);
12739
12740     SetRandomAnimationValue(x, y);
12741
12742     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12743
12744     if (IS_INACTIVE(element))
12745     {
12746       if (IS_ANIMATED(graphic))
12747         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12748
12749       continue;
12750     }
12751
12752     /* this may take place after moving, so 'element' may have changed */
12753     if (IS_CHANGING(x, y) &&
12754         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12755     {
12756       int page = element_info[element].event_page_nr[CE_DELAY];
12757
12758 #if 1
12759       HandleElementChange(x, y, page);
12760 #else
12761       if (CAN_CHANGE(element))
12762         HandleElementChange(x, y, page);
12763
12764       if (HAS_ACTION(element))
12765         ExecuteCustomElementAction(x, y, element, page);
12766 #endif
12767
12768       element = Feld[x][y];
12769       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12770     }
12771
12772 #if 0   // ---------------------------------------------------------------------
12773
12774     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12775     {
12776       StartMoving(x, y);
12777
12778       element = Feld[x][y];
12779       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12780
12781       if (IS_ANIMATED(graphic) &&
12782           !IS_MOVING(x, y) &&
12783           !Stop[x][y])
12784         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12785
12786       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12787         TEST_DrawTwinkleOnField(x, y);
12788     }
12789     else if (IS_MOVING(x, y))
12790       ContinueMoving(x, y);
12791     else
12792     {
12793       switch (element)
12794       {
12795         case EL_ACID:
12796         case EL_EXIT_OPEN:
12797         case EL_EM_EXIT_OPEN:
12798         case EL_SP_EXIT_OPEN:
12799         case EL_STEEL_EXIT_OPEN:
12800         case EL_EM_STEEL_EXIT_OPEN:
12801         case EL_SP_TERMINAL:
12802         case EL_SP_TERMINAL_ACTIVE:
12803         case EL_EXTRA_TIME:
12804         case EL_SHIELD_NORMAL:
12805         case EL_SHIELD_DEADLY:
12806           if (IS_ANIMATED(graphic))
12807             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12808           break;
12809
12810         case EL_DYNAMITE_ACTIVE:
12811         case EL_EM_DYNAMITE_ACTIVE:
12812         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12813         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12814         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12815         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12816         case EL_SP_DISK_RED_ACTIVE:
12817           CheckDynamite(x, y);
12818           break;
12819
12820         case EL_AMOEBA_GROWING:
12821           AmoebeWaechst(x, y);
12822           break;
12823
12824         case EL_AMOEBA_SHRINKING:
12825           AmoebaDisappearing(x, y);
12826           break;
12827
12828 #if !USE_NEW_AMOEBA_CODE
12829         case EL_AMOEBA_WET:
12830         case EL_AMOEBA_DRY:
12831         case EL_AMOEBA_FULL:
12832         case EL_BD_AMOEBA:
12833         case EL_EMC_DRIPPER:
12834           AmoebeAbleger(x, y);
12835           break;
12836 #endif
12837
12838         case EL_GAME_OF_LIFE:
12839         case EL_BIOMAZE:
12840           Life(x, y);
12841           break;
12842
12843         case EL_EXIT_CLOSED:
12844           CheckExit(x, y);
12845           break;
12846
12847         case EL_EM_EXIT_CLOSED:
12848           CheckExitEM(x, y);
12849           break;
12850
12851         case EL_STEEL_EXIT_CLOSED:
12852           CheckExitSteel(x, y);
12853           break;
12854
12855         case EL_EM_STEEL_EXIT_CLOSED:
12856           CheckExitSteelEM(x, y);
12857           break;
12858
12859         case EL_SP_EXIT_CLOSED:
12860           CheckExitSP(x, y);
12861           break;
12862
12863         case EL_EXPANDABLE_WALL_GROWING:
12864         case EL_EXPANDABLE_STEELWALL_GROWING:
12865           MauerWaechst(x, y);
12866           break;
12867
12868         case EL_EXPANDABLE_WALL:
12869         case EL_EXPANDABLE_WALL_HORIZONTAL:
12870         case EL_EXPANDABLE_WALL_VERTICAL:
12871         case EL_EXPANDABLE_WALL_ANY:
12872         case EL_BD_EXPANDABLE_WALL:
12873           MauerAbleger(x, y);
12874           break;
12875
12876         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12877         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12878         case EL_EXPANDABLE_STEELWALL_ANY:
12879           MauerAblegerStahl(x, y);
12880           break;
12881
12882         case EL_FLAMES:
12883           CheckForDragon(x, y);
12884           break;
12885
12886         case EL_EXPLOSION:
12887           break;
12888
12889         case EL_ELEMENT_SNAPPING:
12890         case EL_DIAGONAL_SHRINKING:
12891         case EL_DIAGONAL_GROWING:
12892         {
12893           graphic =
12894             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12895
12896           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12897           break;
12898         }
12899
12900         default:
12901           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12902             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12903           break;
12904       }
12905     }
12906
12907 #else   // ---------------------------------------------------------------------
12908
12909     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12910     {
12911       StartMoving(x, y);
12912
12913       element = Feld[x][y];
12914       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12915
12916       if (IS_ANIMATED(graphic) &&
12917           !IS_MOVING(x, y) &&
12918           !Stop[x][y])
12919         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920
12921       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12922         TEST_DrawTwinkleOnField(x, y);
12923     }
12924     else if ((element == EL_ACID ||
12925               element == EL_EXIT_OPEN ||
12926               element == EL_EM_EXIT_OPEN ||
12927               element == EL_SP_EXIT_OPEN ||
12928               element == EL_STEEL_EXIT_OPEN ||
12929               element == EL_EM_STEEL_EXIT_OPEN ||
12930               element == EL_SP_TERMINAL ||
12931               element == EL_SP_TERMINAL_ACTIVE ||
12932               element == EL_EXTRA_TIME ||
12933               element == EL_SHIELD_NORMAL ||
12934               element == EL_SHIELD_DEADLY) &&
12935              IS_ANIMATED(graphic))
12936       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12937     else if (IS_MOVING(x, y))
12938       ContinueMoving(x, y);
12939     else if (IS_ACTIVE_BOMB(element))
12940       CheckDynamite(x, y);
12941     else if (element == EL_AMOEBA_GROWING)
12942       AmoebeWaechst(x, y);
12943     else if (element == EL_AMOEBA_SHRINKING)
12944       AmoebaDisappearing(x, y);
12945
12946 #if !USE_NEW_AMOEBA_CODE
12947     else if (IS_AMOEBALIVE(element))
12948       AmoebeAbleger(x, y);
12949 #endif
12950
12951     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12952       Life(x, y);
12953     else if (element == EL_EXIT_CLOSED)
12954       CheckExit(x, y);
12955     else if (element == EL_EM_EXIT_CLOSED)
12956       CheckExitEM(x, y);
12957     else if (element == EL_STEEL_EXIT_CLOSED)
12958       CheckExitSteel(x, y);
12959     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12960       CheckExitSteelEM(x, y);
12961     else if (element == EL_SP_EXIT_CLOSED)
12962       CheckExitSP(x, y);
12963     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12964              element == EL_EXPANDABLE_STEELWALL_GROWING)
12965       MauerWaechst(x, y);
12966     else if (element == EL_EXPANDABLE_WALL ||
12967              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12968              element == EL_EXPANDABLE_WALL_VERTICAL ||
12969              element == EL_EXPANDABLE_WALL_ANY ||
12970              element == EL_BD_EXPANDABLE_WALL)
12971       MauerAbleger(x, y);
12972     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12973              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12974              element == EL_EXPANDABLE_STEELWALL_ANY)
12975       MauerAblegerStahl(x, y);
12976     else if (element == EL_FLAMES)
12977       CheckForDragon(x, y);
12978     else if (element == EL_EXPLOSION)
12979       ; /* drawing of correct explosion animation is handled separately */
12980     else if (element == EL_ELEMENT_SNAPPING ||
12981              element == EL_DIAGONAL_SHRINKING ||
12982              element == EL_DIAGONAL_GROWING)
12983     {
12984       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12985
12986       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12987     }
12988     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12989       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12990
12991 #endif  // ---------------------------------------------------------------------
12992
12993     if (IS_BELT_ACTIVE(element))
12994       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12995
12996     if (game.magic_wall_active)
12997     {
12998       int jx = local_player->jx, jy = local_player->jy;
12999
13000       /* play the element sound at the position nearest to the player */
13001       if ((element == EL_MAGIC_WALL_FULL ||
13002            element == EL_MAGIC_WALL_ACTIVE ||
13003            element == EL_MAGIC_WALL_EMPTYING ||
13004            element == EL_BD_MAGIC_WALL_FULL ||
13005            element == EL_BD_MAGIC_WALL_ACTIVE ||
13006            element == EL_BD_MAGIC_WALL_EMPTYING ||
13007            element == EL_DC_MAGIC_WALL_FULL ||
13008            element == EL_DC_MAGIC_WALL_ACTIVE ||
13009            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13010           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13011       {
13012         magic_wall_x = x;
13013         magic_wall_y = y;
13014       }
13015     }
13016   }
13017
13018 #if 0
13019   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13020 #endif
13021
13022 #if USE_NEW_AMOEBA_CODE
13023   /* new experimental amoeba growth stuff */
13024   if (!(FrameCounter % 8))
13025   {
13026     static unsigned long random = 1684108901;
13027
13028     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13029     {
13030       x = RND(lev_fieldx);
13031       y = RND(lev_fieldy);
13032       element = Feld[x][y];
13033
13034       if (!IS_PLAYER(x,y) &&
13035           (element == EL_EMPTY ||
13036            CAN_GROW_INTO(element) ||
13037            element == EL_QUICKSAND_EMPTY ||
13038            element == EL_QUICKSAND_FAST_EMPTY ||
13039            element == EL_ACID_SPLASH_LEFT ||
13040            element == EL_ACID_SPLASH_RIGHT))
13041       {
13042         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13043             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13044             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13045             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13046           Feld[x][y] = EL_AMOEBA_DROP;
13047       }
13048
13049       random = random * 129 + 1;
13050     }
13051   }
13052 #endif
13053
13054 #if 0
13055   if (game.explosions_delayed)
13056 #endif
13057   {
13058     game.explosions_delayed = FALSE;
13059
13060     SCAN_PLAYFIELD(x, y)
13061     {
13062       element = Feld[x][y];
13063
13064       if (ExplodeField[x][y])
13065         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13066       else if (element == EL_EXPLOSION)
13067         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13068
13069       ExplodeField[x][y] = EX_TYPE_NONE;
13070     }
13071
13072     game.explosions_delayed = TRUE;
13073   }
13074
13075   if (game.magic_wall_active)
13076   {
13077     if (!(game.magic_wall_time_left % 4))
13078     {
13079       int element = Feld[magic_wall_x][magic_wall_y];
13080
13081       if (element == EL_BD_MAGIC_WALL_FULL ||
13082           element == EL_BD_MAGIC_WALL_ACTIVE ||
13083           element == EL_BD_MAGIC_WALL_EMPTYING)
13084         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13085       else if (element == EL_DC_MAGIC_WALL_FULL ||
13086                element == EL_DC_MAGIC_WALL_ACTIVE ||
13087                element == EL_DC_MAGIC_WALL_EMPTYING)
13088         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13089       else
13090         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13091     }
13092
13093     if (game.magic_wall_time_left > 0)
13094     {
13095       game.magic_wall_time_left--;
13096
13097       if (!game.magic_wall_time_left)
13098       {
13099         SCAN_PLAYFIELD(x, y)
13100         {
13101           element = Feld[x][y];
13102
13103           if (element == EL_MAGIC_WALL_ACTIVE ||
13104               element == EL_MAGIC_WALL_FULL)
13105           {
13106             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13107             TEST_DrawLevelField(x, y);
13108           }
13109           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13110                    element == EL_BD_MAGIC_WALL_FULL)
13111           {
13112             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13113             TEST_DrawLevelField(x, y);
13114           }
13115           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13116                    element == EL_DC_MAGIC_WALL_FULL)
13117           {
13118             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13119             TEST_DrawLevelField(x, y);
13120           }
13121         }
13122
13123         game.magic_wall_active = FALSE;
13124       }
13125     }
13126   }
13127
13128   if (game.light_time_left > 0)
13129   {
13130     game.light_time_left--;
13131
13132     if (game.light_time_left == 0)
13133       RedrawAllLightSwitchesAndInvisibleElements();
13134   }
13135
13136   if (game.timegate_time_left > 0)
13137   {
13138     game.timegate_time_left--;
13139
13140     if (game.timegate_time_left == 0)
13141       CloseAllOpenTimegates();
13142   }
13143
13144   if (game.lenses_time_left > 0)
13145   {
13146     game.lenses_time_left--;
13147
13148     if (game.lenses_time_left == 0)
13149       RedrawAllInvisibleElementsForLenses();
13150   }
13151
13152   if (game.magnify_time_left > 0)
13153   {
13154     game.magnify_time_left--;
13155
13156     if (game.magnify_time_left == 0)
13157       RedrawAllInvisibleElementsForMagnifier();
13158   }
13159
13160   for (i = 0; i < MAX_PLAYERS; i++)
13161   {
13162     struct PlayerInfo *player = &stored_player[i];
13163
13164     if (SHIELD_ON(player))
13165     {
13166       if (player->shield_deadly_time_left)
13167         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13168       else if (player->shield_normal_time_left)
13169         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13170     }
13171   }
13172
13173 #if USE_DELAYED_GFX_REDRAW
13174   SCAN_PLAYFIELD(x, y)
13175   {
13176 #if 1
13177     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13178 #else
13179     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13180         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13181 #endif
13182     {
13183       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13184          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13185
13186       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13187         DrawLevelField(x, y);
13188
13189       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13190         DrawLevelFieldCrumbled(x, y);
13191
13192       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13193         DrawLevelFieldCrumbledNeighbours(x, y);
13194
13195       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13196         DrawTwinkleOnField(x, y);
13197     }
13198
13199     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13200   }
13201 #endif
13202
13203   CheckLevelTime();
13204
13205   DrawAllPlayers();
13206   PlayAllPlayersSound();
13207
13208   if (options.debug)                    /* calculate frames per second */
13209   {
13210     static unsigned long fps_counter = 0;
13211     static int fps_frames = 0;
13212     unsigned long fps_delay_ms = Counter() - fps_counter;
13213
13214     fps_frames++;
13215
13216     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13217     {
13218       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13219
13220       fps_frames = 0;
13221       fps_counter = Counter();
13222     }
13223
13224     redraw_mask |= REDRAW_FPS;
13225   }
13226
13227   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13228
13229   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13230   {
13231     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13232
13233     local_player->show_envelope = 0;
13234   }
13235
13236 #if 0
13237   debug_print_timestamp(0, "stop main loop profiling ");
13238   printf("----------------------------------------------------------\n");
13239 #endif
13240
13241   /* use random number generator in every frame to make it less predictable */
13242   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13243     RND(1);
13244 }
13245
13246 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13247 {
13248   int min_x = x, min_y = y, max_x = x, max_y = y;
13249   int i;
13250
13251   for (i = 0; i < MAX_PLAYERS; i++)
13252   {
13253     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13254
13255     if (!stored_player[i].active || &stored_player[i] == player)
13256       continue;
13257
13258     min_x = MIN(min_x, jx);
13259     min_y = MIN(min_y, jy);
13260     max_x = MAX(max_x, jx);
13261     max_y = MAX(max_y, jy);
13262   }
13263
13264   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13265 }
13266
13267 static boolean AllPlayersInVisibleScreen()
13268 {
13269   int i;
13270
13271   for (i = 0; i < MAX_PLAYERS; i++)
13272   {
13273     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13274
13275     if (!stored_player[i].active)
13276       continue;
13277
13278     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13279       return FALSE;
13280   }
13281
13282   return TRUE;
13283 }
13284
13285 void ScrollLevel(int dx, int dy)
13286 {
13287 #if 0
13288   /* (directly solved in BlitBitmap() now) */
13289   static Bitmap *bitmap_db_field2 = NULL;
13290   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13291   int x, y;
13292 #else
13293   int x, y;
13294 #endif
13295
13296 #if 0
13297   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13298   /* only horizontal XOR vertical scroll direction allowed */
13299   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13300     return;
13301 #endif
13302
13303 #if 0
13304   /* (directly solved in BlitBitmap() now) */
13305   if (bitmap_db_field2 == NULL)
13306     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13307
13308   /* needed when blitting directly to same bitmap -- should not be needed with
13309      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13310   BlitBitmap(drawto_field, bitmap_db_field2,
13311              FX + TILEX * (dx == -1) - softscroll_offset,
13312              FY + TILEY * (dy == -1) - softscroll_offset,
13313              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13314              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13315              FX + TILEX * (dx == 1) - softscroll_offset,
13316              FY + TILEY * (dy == 1) - softscroll_offset);
13317   BlitBitmap(bitmap_db_field2, drawto_field,
13318              FX + TILEX * (dx == 1) - softscroll_offset,
13319              FY + TILEY * (dy == 1) - softscroll_offset,
13320              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13321              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13322              FX + TILEX * (dx == 1) - softscroll_offset,
13323              FY + TILEY * (dy == 1) - softscroll_offset);
13324
13325 #else
13326
13327 #if 0
13328   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13329   int xsize = (BX2 - BX1 + 1);
13330   int ysize = (BY2 - BY1 + 1);
13331   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13332   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13333   int step  = (start < end ? +1 : -1);
13334
13335   for (i = start; i != end; i += step)
13336   {
13337     BlitBitmap(drawto_field, drawto_field,
13338                FX + TILEX * (dx != 0 ? i + step : 0),
13339                FY + TILEY * (dy != 0 ? i + step : 0),
13340                TILEX * (dx != 0 ? 1 : xsize),
13341                TILEY * (dy != 0 ? 1 : ysize),
13342                FX + TILEX * (dx != 0 ? i : 0),
13343                FY + TILEY * (dy != 0 ? i : 0));
13344   }
13345
13346 #else
13347
13348   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13349
13350   BlitBitmap(drawto_field, drawto_field,
13351              FX + TILEX * (dx == -1) - softscroll_offset,
13352              FY + TILEY * (dy == -1) - softscroll_offset,
13353              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355              FX + TILEX * (dx == 1) - softscroll_offset,
13356              FY + TILEY * (dy == 1) - softscroll_offset);
13357 #endif
13358 #endif
13359
13360   if (dx != 0)
13361   {
13362     x = (dx == 1 ? BX1 : BX2);
13363     for (y = BY1; y <= BY2; y++)
13364       DrawScreenField(x, y);
13365   }
13366
13367   if (dy != 0)
13368   {
13369     y = (dy == 1 ? BY1 : BY2);
13370     for (x = BX1; x <= BX2; x++)
13371       DrawScreenField(x, y);
13372   }
13373
13374   redraw_mask |= REDRAW_FIELD;
13375 }
13376
13377 static boolean canFallDown(struct PlayerInfo *player)
13378 {
13379   int jx = player->jx, jy = player->jy;
13380
13381   return (IN_LEV_FIELD(jx, jy + 1) &&
13382           (IS_FREE(jx, jy + 1) ||
13383            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13384           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13385           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13386 }
13387
13388 static boolean canPassField(int x, int y, int move_dir)
13389 {
13390   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13391   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13392   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13393   int nextx = x + dx;
13394   int nexty = y + dy;
13395   int element = Feld[x][y];
13396
13397   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13398           !CAN_MOVE(element) &&
13399           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13400           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13401           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13402 }
13403
13404 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13405 {
13406   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13407   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13408   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13409   int newx = x + dx;
13410   int newy = y + dy;
13411
13412   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13413           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13414           (IS_DIGGABLE(Feld[newx][newy]) ||
13415            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13416            canPassField(newx, newy, move_dir)));
13417 }
13418
13419 static void CheckGravityMovement(struct PlayerInfo *player)
13420 {
13421 #if USE_PLAYER_GRAVITY
13422   if (player->gravity && !player->programmed_action)
13423 #else
13424   if (game.gravity && !player->programmed_action)
13425 #endif
13426   {
13427     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13428     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13429     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13430     int jx = player->jx, jy = player->jy;
13431     boolean player_is_moving_to_valid_field =
13432       (!player_is_snapping &&
13433        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13434         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13435     boolean player_can_fall_down = canFallDown(player);
13436
13437     if (player_can_fall_down &&
13438         !player_is_moving_to_valid_field)
13439       player->programmed_action = MV_DOWN;
13440   }
13441 }
13442
13443 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13444 {
13445   return CheckGravityMovement(player);
13446
13447 #if USE_PLAYER_GRAVITY
13448   if (player->gravity && !player->programmed_action)
13449 #else
13450   if (game.gravity && !player->programmed_action)
13451 #endif
13452   {
13453     int jx = player->jx, jy = player->jy;
13454     boolean field_under_player_is_free =
13455       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13456     boolean player_is_standing_on_valid_field =
13457       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13458        (IS_WALKABLE(Feld[jx][jy]) &&
13459         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13460
13461     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13462       player->programmed_action = MV_DOWN;
13463   }
13464 }
13465
13466 /*
13467   MovePlayerOneStep()
13468   -----------------------------------------------------------------------------
13469   dx, dy:               direction (non-diagonal) to try to move the player to
13470   real_dx, real_dy:     direction as read from input device (can be diagonal)
13471 */
13472
13473 boolean MovePlayerOneStep(struct PlayerInfo *player,
13474                           int dx, int dy, int real_dx, int real_dy)
13475 {
13476   int jx = player->jx, jy = player->jy;
13477   int new_jx = jx + dx, new_jy = jy + dy;
13478 #if !USE_FIXED_DONT_RUN_INTO
13479   int element;
13480 #endif
13481   int can_move;
13482   boolean player_can_move = !player->cannot_move;
13483
13484   if (!player->active || (!dx && !dy))
13485     return MP_NO_ACTION;
13486
13487   player->MovDir = (dx < 0 ? MV_LEFT :
13488                     dx > 0 ? MV_RIGHT :
13489                     dy < 0 ? MV_UP :
13490                     dy > 0 ? MV_DOWN :  MV_NONE);
13491
13492   if (!IN_LEV_FIELD(new_jx, new_jy))
13493     return MP_NO_ACTION;
13494
13495   if (!player_can_move)
13496   {
13497     if (player->MovPos == 0)
13498     {
13499       player->is_moving = FALSE;
13500       player->is_digging = FALSE;
13501       player->is_collecting = FALSE;
13502       player->is_snapping = FALSE;
13503       player->is_pushing = FALSE;
13504     }
13505   }
13506
13507 #if 1
13508   if (!options.network && game.centered_player_nr == -1 &&
13509       !AllPlayersInSight(player, new_jx, new_jy))
13510     return MP_NO_ACTION;
13511 #else
13512   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13513     return MP_NO_ACTION;
13514 #endif
13515
13516 #if !USE_FIXED_DONT_RUN_INTO
13517   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13518
13519   /* (moved to DigField()) */
13520   if (player_can_move && DONT_RUN_INTO(element))
13521   {
13522     if (element == EL_ACID && dx == 0 && dy == 1)
13523     {
13524       SplashAcid(new_jx, new_jy);
13525       Feld[jx][jy] = EL_PLAYER_1;
13526       InitMovingField(jx, jy, MV_DOWN);
13527       Store[jx][jy] = EL_ACID;
13528       ContinueMoving(jx, jy);
13529       BuryPlayer(player);
13530     }
13531     else
13532       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13533
13534     return MP_MOVING;
13535   }
13536 #endif
13537
13538   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13539   if (can_move != MP_MOVING)
13540     return can_move;
13541
13542   /* check if DigField() has caused relocation of the player */
13543   if (player->jx != jx || player->jy != jy)
13544     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13545
13546   StorePlayer[jx][jy] = 0;
13547   player->last_jx = jx;
13548   player->last_jy = jy;
13549   player->jx = new_jx;
13550   player->jy = new_jy;
13551   StorePlayer[new_jx][new_jy] = player->element_nr;
13552
13553   if (player->move_delay_value_next != -1)
13554   {
13555     player->move_delay_value = player->move_delay_value_next;
13556     player->move_delay_value_next = -1;
13557   }
13558
13559   player->MovPos =
13560     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13561
13562   player->step_counter++;
13563
13564   PlayerVisit[jx][jy] = FrameCounter;
13565
13566 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13567   player->is_moving = TRUE;
13568 #endif
13569
13570 #if 1
13571   /* should better be called in MovePlayer(), but this breaks some tapes */
13572   ScrollPlayer(player, SCROLL_INIT);
13573 #endif
13574
13575   return MP_MOVING;
13576 }
13577
13578 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13579 {
13580   int jx = player->jx, jy = player->jy;
13581   int old_jx = jx, old_jy = jy;
13582   int moved = MP_NO_ACTION;
13583
13584   if (!player->active)
13585     return FALSE;
13586
13587   if (!dx && !dy)
13588   {
13589     if (player->MovPos == 0)
13590     {
13591       player->is_moving = FALSE;
13592       player->is_digging = FALSE;
13593       player->is_collecting = FALSE;
13594       player->is_snapping = FALSE;
13595       player->is_pushing = FALSE;
13596     }
13597
13598     return FALSE;
13599   }
13600
13601   if (player->move_delay > 0)
13602     return FALSE;
13603
13604   player->move_delay = -1;              /* set to "uninitialized" value */
13605
13606   /* store if player is automatically moved to next field */
13607   player->is_auto_moving = (player->programmed_action != MV_NONE);
13608
13609   /* remove the last programmed player action */
13610   player->programmed_action = 0;
13611
13612   if (player->MovPos)
13613   {
13614     /* should only happen if pre-1.2 tape recordings are played */
13615     /* this is only for backward compatibility */
13616
13617     int original_move_delay_value = player->move_delay_value;
13618
13619 #if DEBUG
13620     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13621            tape.counter);
13622 #endif
13623
13624     /* scroll remaining steps with finest movement resolution */
13625     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13626
13627     while (player->MovPos)
13628     {
13629       ScrollPlayer(player, SCROLL_GO_ON);
13630       ScrollScreen(NULL, SCROLL_GO_ON);
13631
13632       AdvanceFrameAndPlayerCounters(player->index_nr);
13633
13634       DrawAllPlayers();
13635       BackToFront();
13636     }
13637
13638     player->move_delay_value = original_move_delay_value;
13639   }
13640
13641   player->is_active = FALSE;
13642
13643   if (player->last_move_dir & MV_HORIZONTAL)
13644   {
13645     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13646       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13647   }
13648   else
13649   {
13650     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13651       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13652   }
13653
13654 #if USE_FIXED_BORDER_RUNNING_GFX
13655   if (!moved && !player->is_active)
13656   {
13657     player->is_moving = FALSE;
13658     player->is_digging = FALSE;
13659     player->is_collecting = FALSE;
13660     player->is_snapping = FALSE;
13661     player->is_pushing = FALSE;
13662   }
13663 #endif
13664
13665   jx = player->jx;
13666   jy = player->jy;
13667
13668 #if 1
13669   if (moved & MP_MOVING && !ScreenMovPos &&
13670       (player->index_nr == game.centered_player_nr ||
13671        game.centered_player_nr == -1))
13672 #else
13673   if (moved & MP_MOVING && !ScreenMovPos &&
13674       (player == local_player || !options.network))
13675 #endif
13676   {
13677     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13678     int offset = game.scroll_delay_value;
13679
13680     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13681     {
13682       /* actual player has left the screen -- scroll in that direction */
13683       if (jx != old_jx)         /* player has moved horizontally */
13684         scroll_x += (jx - old_jx);
13685       else                      /* player has moved vertically */
13686         scroll_y += (jy - old_jy);
13687     }
13688     else
13689     {
13690       if (jx != old_jx)         /* player has moved horizontally */
13691       {
13692         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13693             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13694           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13695
13696         /* don't scroll over playfield boundaries */
13697         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13698           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13699
13700         /* don't scroll more than one field at a time */
13701         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13702
13703         /* don't scroll against the player's moving direction */
13704         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13705             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13706           scroll_x = old_scroll_x;
13707       }
13708       else                      /* player has moved vertically */
13709       {
13710         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13711             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13712           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13713
13714         /* don't scroll over playfield boundaries */
13715         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13716           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13717
13718         /* don't scroll more than one field at a time */
13719         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13720
13721         /* don't scroll against the player's moving direction */
13722         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13723             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13724           scroll_y = old_scroll_y;
13725       }
13726     }
13727
13728     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13729     {
13730 #if 1
13731       if (!options.network && game.centered_player_nr == -1 &&
13732           !AllPlayersInVisibleScreen())
13733       {
13734         scroll_x = old_scroll_x;
13735         scroll_y = old_scroll_y;
13736       }
13737       else
13738 #else
13739       if (!options.network && !AllPlayersInVisibleScreen())
13740       {
13741         scroll_x = old_scroll_x;
13742         scroll_y = old_scroll_y;
13743       }
13744       else
13745 #endif
13746       {
13747         ScrollScreen(player, SCROLL_INIT);
13748         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13749       }
13750     }
13751   }
13752
13753   player->StepFrame = 0;
13754
13755   if (moved & MP_MOVING)
13756   {
13757     if (old_jx != jx && old_jy == jy)
13758       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13759     else if (old_jx == jx && old_jy != jy)
13760       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13761
13762     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13763
13764     player->last_move_dir = player->MovDir;
13765     player->is_moving = TRUE;
13766     player->is_snapping = FALSE;
13767     player->is_switching = FALSE;
13768     player->is_dropping = FALSE;
13769     player->is_dropping_pressed = FALSE;
13770     player->drop_pressed_delay = 0;
13771
13772 #if 0
13773     /* should better be called here than above, but this breaks some tapes */
13774     ScrollPlayer(player, SCROLL_INIT);
13775 #endif
13776   }
13777   else
13778   {
13779     CheckGravityMovementWhenNotMoving(player);
13780
13781     player->is_moving = FALSE;
13782
13783     /* at this point, the player is allowed to move, but cannot move right now
13784        (e.g. because of something blocking the way) -- ensure that the player
13785        is also allowed to move in the next frame (in old versions before 3.1.1,
13786        the player was forced to wait again for eight frames before next try) */
13787
13788     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13789       player->move_delay = 0;   /* allow direct movement in the next frame */
13790   }
13791
13792   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13793     player->move_delay = player->move_delay_value;
13794
13795   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13796   {
13797     TestIfPlayerTouchesBadThing(jx, jy);
13798     TestIfPlayerTouchesCustomElement(jx, jy);
13799   }
13800
13801   if (!player->active)
13802     RemovePlayer(player);
13803
13804   return moved;
13805 }
13806
13807 void ScrollPlayer(struct PlayerInfo *player, int mode)
13808 {
13809   int jx = player->jx, jy = player->jy;
13810   int last_jx = player->last_jx, last_jy = player->last_jy;
13811   int move_stepsize = TILEX / player->move_delay_value;
13812
13813 #if USE_NEW_PLAYER_SPEED
13814   if (!player->active)
13815     return;
13816
13817   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13818     return;
13819 #else
13820   if (!player->active || player->MovPos == 0)
13821     return;
13822 #endif
13823
13824   if (mode == SCROLL_INIT)
13825   {
13826     player->actual_frame_counter = FrameCounter;
13827     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13828
13829     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13830         Feld[last_jx][last_jy] == EL_EMPTY)
13831     {
13832       int last_field_block_delay = 0;   /* start with no blocking at all */
13833       int block_delay_adjustment = player->block_delay_adjustment;
13834
13835       /* if player blocks last field, add delay for exactly one move */
13836       if (player->block_last_field)
13837       {
13838         last_field_block_delay += player->move_delay_value;
13839
13840         /* when blocking enabled, prevent moving up despite gravity */
13841 #if USE_PLAYER_GRAVITY
13842         if (player->gravity && player->MovDir == MV_UP)
13843           block_delay_adjustment = -1;
13844 #else
13845         if (game.gravity && player->MovDir == MV_UP)
13846           block_delay_adjustment = -1;
13847 #endif
13848       }
13849
13850       /* add block delay adjustment (also possible when not blocking) */
13851       last_field_block_delay += block_delay_adjustment;
13852
13853       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13854       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13855     }
13856
13857 #if USE_NEW_PLAYER_SPEED
13858     if (player->MovPos != 0)    /* player has not yet reached destination */
13859       return;
13860 #else
13861     return;
13862 #endif
13863   }
13864   else if (!FrameReached(&player->actual_frame_counter, 1))
13865     return;
13866
13867 #if USE_NEW_PLAYER_SPEED
13868   if (player->MovPos != 0)
13869   {
13870     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13871     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13872
13873     /* before DrawPlayer() to draw correct player graphic for this case */
13874     if (player->MovPos == 0)
13875       CheckGravityMovement(player);
13876   }
13877 #else
13878   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13879   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13880
13881   /* before DrawPlayer() to draw correct player graphic for this case */
13882   if (player->MovPos == 0)
13883     CheckGravityMovement(player);
13884 #endif
13885
13886   if (player->MovPos == 0)      /* player reached destination field */
13887   {
13888     if (player->move_delay_reset_counter > 0)
13889     {
13890       player->move_delay_reset_counter--;
13891
13892       if (player->move_delay_reset_counter == 0)
13893       {
13894         /* continue with normal speed after quickly moving through gate */
13895         HALVE_PLAYER_SPEED(player);
13896
13897         /* be able to make the next move without delay */
13898         player->move_delay = 0;
13899       }
13900     }
13901
13902     player->last_jx = jx;
13903     player->last_jy = jy;
13904
13905     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13906         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13907 #if 1
13908         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13909 #endif
13910         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13911         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13912 #if 1
13913         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13914 #endif
13915         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13916         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13917     {
13918       DrawPlayer(player);       /* needed here only to cleanup last field */
13919       RemovePlayer(player);
13920
13921       if (local_player->friends_still_needed == 0 ||
13922           IS_SP_ELEMENT(Feld[jx][jy]))
13923         PlayerWins(player);
13924     }
13925
13926     /* this breaks one level: "machine", level 000 */
13927     {
13928       int move_direction = player->MovDir;
13929       int enter_side = MV_DIR_OPPOSITE(move_direction);
13930       int leave_side = move_direction;
13931       int old_jx = last_jx;
13932       int old_jy = last_jy;
13933       int old_element = Feld[old_jx][old_jy];
13934       int new_element = Feld[jx][jy];
13935
13936       if (IS_CUSTOM_ELEMENT(old_element))
13937         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13938                                    CE_LEFT_BY_PLAYER,
13939                                    player->index_bit, leave_side);
13940
13941       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13942                                           CE_PLAYER_LEAVES_X,
13943                                           player->index_bit, leave_side);
13944
13945       if (IS_CUSTOM_ELEMENT(new_element))
13946         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13947                                    player->index_bit, enter_side);
13948
13949       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13950                                           CE_PLAYER_ENTERS_X,
13951                                           player->index_bit, enter_side);
13952
13953 #if USE_FIX_CE_ACTION_WITH_PLAYER
13954       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13955                                         CE_MOVE_OF_X, move_direction);
13956 #else
13957       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13958                                         CE_MOVE_OF_X, move_direction);
13959 #endif
13960     }
13961
13962     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13963     {
13964       TestIfPlayerTouchesBadThing(jx, jy);
13965       TestIfPlayerTouchesCustomElement(jx, jy);
13966
13967       /* needed because pushed element has not yet reached its destination,
13968          so it would trigger a change event at its previous field location */
13969       if (!player->is_pushing)
13970         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13971
13972       if (!player->active)
13973         RemovePlayer(player);
13974     }
13975
13976     if (!local_player->LevelSolved && level.use_step_counter)
13977     {
13978       int i;
13979
13980       TimePlayed++;
13981
13982       if (TimeLeft > 0)
13983       {
13984         TimeLeft--;
13985
13986         if (TimeLeft <= 10 && setup.time_limit)
13987           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13988
13989 #if 1
13990         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13991
13992         DisplayGameControlValues();
13993 #else
13994         DrawGameValue_Time(TimeLeft);
13995 #endif
13996
13997         if (!TimeLeft && setup.time_limit)
13998           for (i = 0; i < MAX_PLAYERS; i++)
13999             KillPlayer(&stored_player[i]);
14000       }
14001 #if 1
14002       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14003       {
14004         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14005
14006         DisplayGameControlValues();
14007       }
14008 #else
14009       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14010         DrawGameValue_Time(TimePlayed);
14011 #endif
14012     }
14013
14014     if (tape.single_step && tape.recording && !tape.pausing &&
14015         !player->programmed_action)
14016       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14017   }
14018 }
14019
14020 void ScrollScreen(struct PlayerInfo *player, int mode)
14021 {
14022   static unsigned long screen_frame_counter = 0;
14023
14024   if (mode == SCROLL_INIT)
14025   {
14026     /* set scrolling step size according to actual player's moving speed */
14027     ScrollStepSize = TILEX / player->move_delay_value;
14028
14029     screen_frame_counter = FrameCounter;
14030     ScreenMovDir = player->MovDir;
14031     ScreenMovPos = player->MovPos;
14032     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14033     return;
14034   }
14035   else if (!FrameReached(&screen_frame_counter, 1))
14036     return;
14037
14038   if (ScreenMovPos)
14039   {
14040     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14041     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14042     redraw_mask |= REDRAW_FIELD;
14043   }
14044   else
14045     ScreenMovDir = MV_NONE;
14046 }
14047
14048 void TestIfPlayerTouchesCustomElement(int x, int y)
14049 {
14050   static int xy[4][2] =
14051   {
14052     { 0, -1 },
14053     { -1, 0 },
14054     { +1, 0 },
14055     { 0, +1 }
14056   };
14057   static int trigger_sides[4][2] =
14058   {
14059     /* center side       border side */
14060     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14061     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14062     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14063     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14064   };
14065   static int touch_dir[4] =
14066   {
14067     MV_LEFT | MV_RIGHT,
14068     MV_UP   | MV_DOWN,
14069     MV_UP   | MV_DOWN,
14070     MV_LEFT | MV_RIGHT
14071   };
14072   int center_element = Feld[x][y];      /* should always be non-moving! */
14073   int i;
14074
14075   for (i = 0; i < NUM_DIRECTIONS; i++)
14076   {
14077     int xx = x + xy[i][0];
14078     int yy = y + xy[i][1];
14079     int center_side = trigger_sides[i][0];
14080     int border_side = trigger_sides[i][1];
14081     int border_element;
14082
14083     if (!IN_LEV_FIELD(xx, yy))
14084       continue;
14085
14086     if (IS_PLAYER(x, y))                /* player found at center element */
14087     {
14088       struct PlayerInfo *player = PLAYERINFO(x, y);
14089
14090       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14091         border_element = Feld[xx][yy];          /* may be moving! */
14092       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14093         border_element = Feld[xx][yy];
14094       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14095         border_element = MovingOrBlocked2Element(xx, yy);
14096       else
14097         continue;               /* center and border element do not touch */
14098
14099       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14100                                  player->index_bit, border_side);
14101       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14102                                           CE_PLAYER_TOUCHES_X,
14103                                           player->index_bit, border_side);
14104
14105 #if USE_FIX_CE_ACTION_WITH_PLAYER
14106       {
14107         /* use player element that is initially defined in the level playfield,
14108            not the player element that corresponds to the runtime player number
14109            (example: a level that contains EL_PLAYER_3 as the only player would
14110            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14111         int player_element = PLAYERINFO(x, y)->initial_element;
14112
14113         CheckElementChangeBySide(xx, yy, border_element, player_element,
14114                                  CE_TOUCHING_X, border_side);
14115       }
14116 #endif
14117     }
14118     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14119     {
14120       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14121
14122       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14123       {
14124         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14125           continue;             /* center and border element do not touch */
14126       }
14127
14128       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14129                                  player->index_bit, center_side);
14130       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14131                                           CE_PLAYER_TOUCHES_X,
14132                                           player->index_bit, center_side);
14133
14134 #if USE_FIX_CE_ACTION_WITH_PLAYER
14135       {
14136         /* use player element that is initially defined in the level playfield,
14137            not the player element that corresponds to the runtime player number
14138            (example: a level that contains EL_PLAYER_3 as the only player would
14139            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14140         int player_element = PLAYERINFO(xx, yy)->initial_element;
14141
14142         CheckElementChangeBySide(x, y, center_element, player_element,
14143                                  CE_TOUCHING_X, center_side);
14144       }
14145 #endif
14146
14147       break;
14148     }
14149   }
14150 }
14151
14152 #if USE_ELEMENT_TOUCHING_BUGFIX
14153
14154 void TestIfElementTouchesCustomElement(int x, int y)
14155 {
14156   static int xy[4][2] =
14157   {
14158     { 0, -1 },
14159     { -1, 0 },
14160     { +1, 0 },
14161     { 0, +1 }
14162   };
14163   static int trigger_sides[4][2] =
14164   {
14165     /* center side      border side */
14166     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14167     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14168     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14169     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14170   };
14171   static int touch_dir[4] =
14172   {
14173     MV_LEFT | MV_RIGHT,
14174     MV_UP   | MV_DOWN,
14175     MV_UP   | MV_DOWN,
14176     MV_LEFT | MV_RIGHT
14177   };
14178   boolean change_center_element = FALSE;
14179   int center_element = Feld[x][y];      /* should always be non-moving! */
14180   int border_element_old[NUM_DIRECTIONS];
14181   int i;
14182
14183   for (i = 0; i < NUM_DIRECTIONS; i++)
14184   {
14185     int xx = x + xy[i][0];
14186     int yy = y + xy[i][1];
14187     int border_element;
14188
14189     border_element_old[i] = -1;
14190
14191     if (!IN_LEV_FIELD(xx, yy))
14192       continue;
14193
14194     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14195       border_element = Feld[xx][yy];    /* may be moving! */
14196     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14197       border_element = Feld[xx][yy];
14198     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14199       border_element = MovingOrBlocked2Element(xx, yy);
14200     else
14201       continue;                 /* center and border element do not touch */
14202
14203     border_element_old[i] = border_element;
14204   }
14205
14206   for (i = 0; i < NUM_DIRECTIONS; i++)
14207   {
14208     int xx = x + xy[i][0];
14209     int yy = y + xy[i][1];
14210     int center_side = trigger_sides[i][0];
14211     int border_element = border_element_old[i];
14212
14213     if (border_element == -1)
14214       continue;
14215
14216     /* check for change of border element */
14217     CheckElementChangeBySide(xx, yy, border_element, center_element,
14218                              CE_TOUCHING_X, center_side);
14219
14220     /* (center element cannot be player, so we dont have to check this here) */
14221   }
14222
14223   for (i = 0; i < NUM_DIRECTIONS; i++)
14224   {
14225     int xx = x + xy[i][0];
14226     int yy = y + xy[i][1];
14227     int border_side = trigger_sides[i][1];
14228     int border_element = border_element_old[i];
14229
14230     if (border_element == -1)
14231       continue;
14232
14233     /* check for change of center element (but change it only once) */
14234     if (!change_center_element)
14235       change_center_element =
14236         CheckElementChangeBySide(x, y, center_element, border_element,
14237                                  CE_TOUCHING_X, border_side);
14238
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14240     if (IS_PLAYER(xx, yy))
14241     {
14242       /* use player element that is initially defined in the level playfield,
14243          not the player element that corresponds to the runtime player number
14244          (example: a level that contains EL_PLAYER_3 as the only player would
14245          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14246       int player_element = PLAYERINFO(xx, yy)->initial_element;
14247
14248       CheckElementChangeBySide(x, y, center_element, player_element,
14249                                CE_TOUCHING_X, border_side);
14250     }
14251 #endif
14252   }
14253 }
14254
14255 #else
14256
14257 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14258 {
14259   static int xy[4][2] =
14260   {
14261     { 0, -1 },
14262     { -1, 0 },
14263     { +1, 0 },
14264     { 0, +1 }
14265   };
14266   static int trigger_sides[4][2] =
14267   {
14268     /* center side      border side */
14269     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14270     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14271     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14272     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14273   };
14274   static int touch_dir[4] =
14275   {
14276     MV_LEFT | MV_RIGHT,
14277     MV_UP   | MV_DOWN,
14278     MV_UP   | MV_DOWN,
14279     MV_LEFT | MV_RIGHT
14280   };
14281   boolean change_center_element = FALSE;
14282   int center_element = Feld[x][y];      /* should always be non-moving! */
14283   int i;
14284
14285   for (i = 0; i < NUM_DIRECTIONS; i++)
14286   {
14287     int xx = x + xy[i][0];
14288     int yy = y + xy[i][1];
14289     int center_side = trigger_sides[i][0];
14290     int border_side = trigger_sides[i][1];
14291     int border_element;
14292
14293     if (!IN_LEV_FIELD(xx, yy))
14294       continue;
14295
14296     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297       border_element = Feld[xx][yy];    /* may be moving! */
14298     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299       border_element = Feld[xx][yy];
14300     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14301       border_element = MovingOrBlocked2Element(xx, yy);
14302     else
14303       continue;                 /* center and border element do not touch */
14304
14305     /* check for change of center element (but change it only once) */
14306     if (!change_center_element)
14307       change_center_element =
14308         CheckElementChangeBySide(x, y, center_element, border_element,
14309                                  CE_TOUCHING_X, border_side);
14310
14311     /* check for change of border element */
14312     CheckElementChangeBySide(xx, yy, border_element, center_element,
14313                              CE_TOUCHING_X, center_side);
14314   }
14315 }
14316
14317 #endif
14318
14319 void TestIfElementHitsCustomElement(int x, int y, int direction)
14320 {
14321   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14322   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14323   int hitx = x + dx, hity = y + dy;
14324   int hitting_element = Feld[x][y];
14325   int touched_element;
14326
14327   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14328     return;
14329
14330   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14331                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14332
14333   if (IN_LEV_FIELD(hitx, hity))
14334   {
14335     int opposite_direction = MV_DIR_OPPOSITE(direction);
14336     int hitting_side = direction;
14337     int touched_side = opposite_direction;
14338     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14339                           MovDir[hitx][hity] != direction ||
14340                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14341
14342     object_hit = TRUE;
14343
14344     if (object_hit)
14345     {
14346       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14347                                CE_HITTING_X, touched_side);
14348
14349       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14350                                CE_HIT_BY_X, hitting_side);
14351
14352       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14353                                CE_HIT_BY_SOMETHING, opposite_direction);
14354
14355 #if USE_FIX_CE_ACTION_WITH_PLAYER
14356       if (IS_PLAYER(hitx, hity))
14357       {
14358         /* use player element that is initially defined in the level playfield,
14359            not the player element that corresponds to the runtime player number
14360            (example: a level that contains EL_PLAYER_3 as the only player would
14361            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14362         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14363
14364         CheckElementChangeBySide(x, y, hitting_element, player_element,
14365                                  CE_HITTING_X, touched_side);
14366       }
14367 #endif
14368     }
14369   }
14370
14371   /* "hitting something" is also true when hitting the playfield border */
14372   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14373                            CE_HITTING_SOMETHING, direction);
14374 }
14375
14376 #if 0
14377 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14378 {
14379   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14380   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14381   int hitx = x + dx, hity = y + dy;
14382   int hitting_element = Feld[x][y];
14383   int touched_element;
14384 #if 0
14385   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14386                         !IS_FREE(hitx, hity) &&
14387                         (!IS_MOVING(hitx, hity) ||
14388                          MovDir[hitx][hity] != direction ||
14389                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14390 #endif
14391
14392   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14393     return;
14394
14395 #if 0
14396   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14397     return;
14398 #endif
14399
14400   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14401                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14402
14403   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14404                            EP_CAN_SMASH_EVERYTHING, direction);
14405
14406   if (IN_LEV_FIELD(hitx, hity))
14407   {
14408     int opposite_direction = MV_DIR_OPPOSITE(direction);
14409     int hitting_side = direction;
14410     int touched_side = opposite_direction;
14411 #if 0
14412     int touched_element = MovingOrBlocked2Element(hitx, hity);
14413 #endif
14414 #if 1
14415     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14416                           MovDir[hitx][hity] != direction ||
14417                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14418
14419     object_hit = TRUE;
14420 #endif
14421
14422     if (object_hit)
14423     {
14424       int i;
14425
14426       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14427                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14428
14429       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14430                                CE_OTHER_IS_SMASHING, touched_side);
14431
14432       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14433                                CE_OTHER_GETS_SMASHED, hitting_side);
14434     }
14435   }
14436 }
14437 #endif
14438
14439 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14440 {
14441   int i, kill_x = -1, kill_y = -1;
14442
14443   int bad_element = -1;
14444   static int test_xy[4][2] =
14445   {
14446     { 0, -1 },
14447     { -1, 0 },
14448     { +1, 0 },
14449     { 0, +1 }
14450   };
14451   static int test_dir[4] =
14452   {
14453     MV_UP,
14454     MV_LEFT,
14455     MV_RIGHT,
14456     MV_DOWN
14457   };
14458
14459   for (i = 0; i < NUM_DIRECTIONS; i++)
14460   {
14461     int test_x, test_y, test_move_dir, test_element;
14462
14463     test_x = good_x + test_xy[i][0];
14464     test_y = good_y + test_xy[i][1];
14465
14466     if (!IN_LEV_FIELD(test_x, test_y))
14467       continue;
14468
14469     test_move_dir =
14470       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14471
14472     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14473
14474     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14475        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14476     */
14477     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14478         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14479     {
14480       kill_x = test_x;
14481       kill_y = test_y;
14482       bad_element = test_element;
14483
14484       break;
14485     }
14486   }
14487
14488   if (kill_x != -1 || kill_y != -1)
14489   {
14490     if (IS_PLAYER(good_x, good_y))
14491     {
14492       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14493
14494       if (player->shield_deadly_time_left > 0 &&
14495           !IS_INDESTRUCTIBLE(bad_element))
14496         Bang(kill_x, kill_y);
14497       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14498         KillPlayer(player);
14499     }
14500     else
14501       Bang(good_x, good_y);
14502   }
14503 }
14504
14505 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14506 {
14507   int i, kill_x = -1, kill_y = -1;
14508   int bad_element = Feld[bad_x][bad_y];
14509   static int test_xy[4][2] =
14510   {
14511     { 0, -1 },
14512     { -1, 0 },
14513     { +1, 0 },
14514     { 0, +1 }
14515   };
14516   static int touch_dir[4] =
14517   {
14518     MV_LEFT | MV_RIGHT,
14519     MV_UP   | MV_DOWN,
14520     MV_UP   | MV_DOWN,
14521     MV_LEFT | MV_RIGHT
14522   };
14523   static int test_dir[4] =
14524   {
14525     MV_UP,
14526     MV_LEFT,
14527     MV_RIGHT,
14528     MV_DOWN
14529   };
14530
14531   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14532     return;
14533
14534   for (i = 0; i < NUM_DIRECTIONS; i++)
14535   {
14536     int test_x, test_y, test_move_dir, test_element;
14537
14538     test_x = bad_x + test_xy[i][0];
14539     test_y = bad_y + test_xy[i][1];
14540
14541     if (!IN_LEV_FIELD(test_x, test_y))
14542       continue;
14543
14544     test_move_dir =
14545       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14546
14547     test_element = Feld[test_x][test_y];
14548
14549     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14550        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14551     */
14552     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14553         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14554     {
14555       /* good thing is player or penguin that does not move away */
14556       if (IS_PLAYER(test_x, test_y))
14557       {
14558         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14559
14560         if (bad_element == EL_ROBOT && player->is_moving)
14561           continue;     /* robot does not kill player if he is moving */
14562
14563         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14564         {
14565           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14566             continue;           /* center and border element do not touch */
14567         }
14568
14569         kill_x = test_x;
14570         kill_y = test_y;
14571
14572         break;
14573       }
14574       else if (test_element == EL_PENGUIN)
14575       {
14576         kill_x = test_x;
14577         kill_y = test_y;
14578
14579         break;
14580       }
14581     }
14582   }
14583
14584   if (kill_x != -1 || kill_y != -1)
14585   {
14586     if (IS_PLAYER(kill_x, kill_y))
14587     {
14588       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14589
14590       if (player->shield_deadly_time_left > 0 &&
14591           !IS_INDESTRUCTIBLE(bad_element))
14592         Bang(bad_x, bad_y);
14593       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14594         KillPlayer(player);
14595     }
14596     else
14597       Bang(kill_x, kill_y);
14598   }
14599 }
14600
14601 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14602 {
14603   int bad_element = Feld[bad_x][bad_y];
14604   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14605   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14606   int test_x = bad_x + dx, test_y = bad_y + dy;
14607   int test_move_dir, test_element;
14608   int kill_x = -1, kill_y = -1;
14609
14610   if (!IN_LEV_FIELD(test_x, test_y))
14611     return;
14612
14613   test_move_dir =
14614     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14615
14616   test_element = Feld[test_x][test_y];
14617
14618   if (test_move_dir != bad_move_dir)
14619   {
14620     /* good thing can be player or penguin that does not move away */
14621     if (IS_PLAYER(test_x, test_y))
14622     {
14623       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14624
14625       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14626          player as being hit when he is moving towards the bad thing, because
14627          the "get hit by" condition would be lost after the player stops) */
14628       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14629         return;         /* player moves away from bad thing */
14630
14631       kill_x = test_x;
14632       kill_y = test_y;
14633     }
14634     else if (test_element == EL_PENGUIN)
14635     {
14636       kill_x = test_x;
14637       kill_y = test_y;
14638     }
14639   }
14640
14641   if (kill_x != -1 || kill_y != -1)
14642   {
14643     if (IS_PLAYER(kill_x, kill_y))
14644     {
14645       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14646
14647       if (player->shield_deadly_time_left > 0 &&
14648           !IS_INDESTRUCTIBLE(bad_element))
14649         Bang(bad_x, bad_y);
14650       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14651         KillPlayer(player);
14652     }
14653     else
14654       Bang(kill_x, kill_y);
14655   }
14656 }
14657
14658 void TestIfPlayerTouchesBadThing(int x, int y)
14659 {
14660   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14661 }
14662
14663 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14664 {
14665   TestIfGoodThingHitsBadThing(x, y, move_dir);
14666 }
14667
14668 void TestIfBadThingTouchesPlayer(int x, int y)
14669 {
14670   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14671 }
14672
14673 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14674 {
14675   TestIfBadThingHitsGoodThing(x, y, move_dir);
14676 }
14677
14678 void TestIfFriendTouchesBadThing(int x, int y)
14679 {
14680   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14681 }
14682
14683 void TestIfBadThingTouchesFriend(int x, int y)
14684 {
14685   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14686 }
14687
14688 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14689 {
14690   int i, kill_x = bad_x, kill_y = bad_y;
14691   static int xy[4][2] =
14692   {
14693     { 0, -1 },
14694     { -1, 0 },
14695     { +1, 0 },
14696     { 0, +1 }
14697   };
14698
14699   for (i = 0; i < NUM_DIRECTIONS; i++)
14700   {
14701     int x, y, element;
14702
14703     x = bad_x + xy[i][0];
14704     y = bad_y + xy[i][1];
14705     if (!IN_LEV_FIELD(x, y))
14706       continue;
14707
14708     element = Feld[x][y];
14709     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14710         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14711     {
14712       kill_x = x;
14713       kill_y = y;
14714       break;
14715     }
14716   }
14717
14718   if (kill_x != bad_x || kill_y != bad_y)
14719     Bang(bad_x, bad_y);
14720 }
14721
14722 void KillPlayer(struct PlayerInfo *player)
14723 {
14724   int jx = player->jx, jy = player->jy;
14725
14726   if (!player->active)
14727     return;
14728
14729 #if 0
14730   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14731          player->killed, player->active, player->reanimated);
14732 #endif
14733
14734   /* the following code was introduced to prevent an infinite loop when calling
14735      -> Bang()
14736      -> CheckTriggeredElementChangeExt()
14737      -> ExecuteCustomElementAction()
14738      -> KillPlayer()
14739      -> (infinitely repeating the above sequence of function calls)
14740      which occurs when killing the player while having a CE with the setting
14741      "kill player X when explosion of <player X>"; the solution using a new
14742      field "player->killed" was chosen for backwards compatibility, although
14743      clever use of the fields "player->active" etc. would probably also work */
14744 #if 1
14745   if (player->killed)
14746     return;
14747 #endif
14748
14749   player->killed = TRUE;
14750
14751   /* remove accessible field at the player's position */
14752   Feld[jx][jy] = EL_EMPTY;
14753
14754   /* deactivate shield (else Bang()/Explode() would not work right) */
14755   player->shield_normal_time_left = 0;
14756   player->shield_deadly_time_left = 0;
14757
14758 #if 0
14759   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14760          player->killed, player->active, player->reanimated);
14761 #endif
14762
14763   Bang(jx, jy);
14764
14765 #if 0
14766   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14767          player->killed, player->active, player->reanimated);
14768 #endif
14769
14770 #if USE_PLAYER_REANIMATION
14771 #if 1
14772   if (player->reanimated)       /* killed player may have been reanimated */
14773     player->killed = player->reanimated = FALSE;
14774   else
14775     BuryPlayer(player);
14776 #else
14777   if (player->killed)           /* player may have been reanimated */
14778     BuryPlayer(player);
14779 #endif
14780 #else
14781   BuryPlayer(player);
14782 #endif
14783 }
14784
14785 static void KillPlayerUnlessEnemyProtected(int x, int y)
14786 {
14787   if (!PLAYER_ENEMY_PROTECTED(x, y))
14788     KillPlayer(PLAYERINFO(x, y));
14789 }
14790
14791 static void KillPlayerUnlessExplosionProtected(int x, int y)
14792 {
14793   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14794     KillPlayer(PLAYERINFO(x, y));
14795 }
14796
14797 void BuryPlayer(struct PlayerInfo *player)
14798 {
14799   int jx = player->jx, jy = player->jy;
14800
14801   if (!player->active)
14802     return;
14803
14804   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14805   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14806
14807   player->GameOver = TRUE;
14808   RemovePlayer(player);
14809 }
14810
14811 void RemovePlayer(struct PlayerInfo *player)
14812 {
14813   int jx = player->jx, jy = player->jy;
14814   int i, found = FALSE;
14815
14816   player->present = FALSE;
14817   player->active = FALSE;
14818
14819   if (!ExplodeField[jx][jy])
14820     StorePlayer[jx][jy] = 0;
14821
14822   if (player->is_moving)
14823     TEST_DrawLevelField(player->last_jx, player->last_jy);
14824
14825   for (i = 0; i < MAX_PLAYERS; i++)
14826     if (stored_player[i].active)
14827       found = TRUE;
14828
14829   if (!found)
14830     AllPlayersGone = TRUE;
14831
14832   ExitX = ZX = jx;
14833   ExitY = ZY = jy;
14834 }
14835
14836 #if USE_NEW_SNAP_DELAY
14837 static void setFieldForSnapping(int x, int y, int element, int direction)
14838 {
14839   struct ElementInfo *ei = &element_info[element];
14840   int direction_bit = MV_DIR_TO_BIT(direction);
14841   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14842   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14843                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14844
14845   Feld[x][y] = EL_ELEMENT_SNAPPING;
14846   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14847
14848   ResetGfxAnimation(x, y);
14849
14850   GfxElement[x][y] = element;
14851   GfxAction[x][y] = action;
14852   GfxDir[x][y] = direction;
14853   GfxFrame[x][y] = -1;
14854 }
14855 #endif
14856
14857 /*
14858   =============================================================================
14859   checkDiagonalPushing()
14860   -----------------------------------------------------------------------------
14861   check if diagonal input device direction results in pushing of object
14862   (by checking if the alternative direction is walkable, diggable, ...)
14863   =============================================================================
14864 */
14865
14866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14867                                     int x, int y, int real_dx, int real_dy)
14868 {
14869   int jx, jy, dx, dy, xx, yy;
14870
14871   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14872     return TRUE;
14873
14874   /* diagonal direction: check alternative direction */
14875   jx = player->jx;
14876   jy = player->jy;
14877   dx = x - jx;
14878   dy = y - jy;
14879   xx = jx + (dx == 0 ? real_dx : 0);
14880   yy = jy + (dy == 0 ? real_dy : 0);
14881
14882   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14883 }
14884
14885 /*
14886   =============================================================================
14887   DigField()
14888   -----------------------------------------------------------------------------
14889   x, y:                 field next to player (non-diagonal) to try to dig to
14890   real_dx, real_dy:     direction as read from input device (can be diagonal)
14891   =============================================================================
14892 */
14893
14894 static int DigField(struct PlayerInfo *player,
14895                     int oldx, int oldy, int x, int y,
14896                     int real_dx, int real_dy, int mode)
14897 {
14898   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14899   boolean player_was_pushing = player->is_pushing;
14900   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14901   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14902   int jx = oldx, jy = oldy;
14903   int dx = x - jx, dy = y - jy;
14904   int nextx = x + dx, nexty = y + dy;
14905   int move_direction = (dx == -1 ? MV_LEFT  :
14906                         dx == +1 ? MV_RIGHT :
14907                         dy == -1 ? MV_UP    :
14908                         dy == +1 ? MV_DOWN  : MV_NONE);
14909   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14910   int dig_side = MV_DIR_OPPOSITE(move_direction);
14911   int old_element = Feld[jx][jy];
14912 #if USE_FIXED_DONT_RUN_INTO
14913   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14914 #else
14915   int element;
14916 #endif
14917   int collect_count;
14918
14919   if (is_player)                /* function can also be called by EL_PENGUIN */
14920   {
14921     if (player->MovPos == 0)
14922     {
14923       player->is_digging = FALSE;
14924       player->is_collecting = FALSE;
14925     }
14926
14927     if (player->MovPos == 0)    /* last pushing move finished */
14928       player->is_pushing = FALSE;
14929
14930     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14931     {
14932       player->is_switching = FALSE;
14933       player->push_delay = -1;
14934
14935       return MP_NO_ACTION;
14936     }
14937   }
14938
14939 #if !USE_FIXED_DONT_RUN_INTO
14940   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14941     return MP_NO_ACTION;
14942 #endif
14943
14944   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14945     old_element = Back[jx][jy];
14946
14947   /* in case of element dropped at player position, check background */
14948   else if (Back[jx][jy] != EL_EMPTY &&
14949            game.engine_version >= VERSION_IDENT(2,2,0,0))
14950     old_element = Back[jx][jy];
14951
14952   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14953     return MP_NO_ACTION;        /* field has no opening in this direction */
14954
14955   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14956     return MP_NO_ACTION;        /* field has no opening in this direction */
14957
14958 #if USE_FIXED_DONT_RUN_INTO
14959   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14960   {
14961     SplashAcid(x, y);
14962
14963     Feld[jx][jy] = player->artwork_element;
14964     InitMovingField(jx, jy, MV_DOWN);
14965     Store[jx][jy] = EL_ACID;
14966     ContinueMoving(jx, jy);
14967     BuryPlayer(player);
14968
14969     return MP_DONT_RUN_INTO;
14970   }
14971 #endif
14972
14973 #if USE_FIXED_DONT_RUN_INTO
14974   if (player_can_move && DONT_RUN_INTO(element))
14975   {
14976     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14977
14978     return MP_DONT_RUN_INTO;
14979   }
14980 #endif
14981
14982 #if USE_FIXED_DONT_RUN_INTO
14983   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14984     return MP_NO_ACTION;
14985 #endif
14986
14987 #if !USE_FIXED_DONT_RUN_INTO
14988   element = Feld[x][y];
14989 #endif
14990
14991   collect_count = element_info[element].collect_count_initial;
14992
14993   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14994     return MP_NO_ACTION;
14995
14996   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14997     player_can_move = player_can_move_or_snap;
14998
14999   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15000       game.engine_version >= VERSION_IDENT(2,2,0,0))
15001   {
15002     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15003                                player->index_bit, dig_side);
15004     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15005                                         player->index_bit, dig_side);
15006
15007     if (element == EL_DC_LANDMINE)
15008       Bang(x, y);
15009
15010     if (Feld[x][y] != element)          /* field changed by snapping */
15011       return MP_ACTION;
15012
15013     return MP_NO_ACTION;
15014   }
15015
15016 #if USE_PLAYER_GRAVITY
15017   if (player->gravity && is_player && !player->is_auto_moving &&
15018       canFallDown(player) && move_direction != MV_DOWN &&
15019       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15020     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15021 #else
15022   if (game.gravity && is_player && !player->is_auto_moving &&
15023       canFallDown(player) && move_direction != MV_DOWN &&
15024       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15025     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15026 #endif
15027
15028   if (player_can_move &&
15029       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15030   {
15031     int sound_element = SND_ELEMENT(element);
15032     int sound_action = ACTION_WALKING;
15033
15034     if (IS_RND_GATE(element))
15035     {
15036       if (!player->key[RND_GATE_NR(element)])
15037         return MP_NO_ACTION;
15038     }
15039     else if (IS_RND_GATE_GRAY(element))
15040     {
15041       if (!player->key[RND_GATE_GRAY_NR(element)])
15042         return MP_NO_ACTION;
15043     }
15044     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15045     {
15046       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15047         return MP_NO_ACTION;
15048     }
15049     else if (element == EL_EXIT_OPEN ||
15050              element == EL_EM_EXIT_OPEN ||
15051 #if 1
15052              element == EL_EM_EXIT_OPENING ||
15053 #endif
15054              element == EL_STEEL_EXIT_OPEN ||
15055              element == EL_EM_STEEL_EXIT_OPEN ||
15056 #if 1
15057              element == EL_EM_STEEL_EXIT_OPENING ||
15058 #endif
15059              element == EL_SP_EXIT_OPEN ||
15060              element == EL_SP_EXIT_OPENING)
15061     {
15062       sound_action = ACTION_PASSING;    /* player is passing exit */
15063     }
15064     else if (element == EL_EMPTY)
15065     {
15066       sound_action = ACTION_MOVING;             /* nothing to walk on */
15067     }
15068
15069     /* play sound from background or player, whatever is available */
15070     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15071       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15072     else
15073       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15074   }
15075   else if (player_can_move &&
15076            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15077   {
15078     if (!ACCESS_FROM(element, opposite_direction))
15079       return MP_NO_ACTION;      /* field not accessible from this direction */
15080
15081     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15082       return MP_NO_ACTION;
15083
15084     if (IS_EM_GATE(element))
15085     {
15086       if (!player->key[EM_GATE_NR(element)])
15087         return MP_NO_ACTION;
15088     }
15089     else if (IS_EM_GATE_GRAY(element))
15090     {
15091       if (!player->key[EM_GATE_GRAY_NR(element)])
15092         return MP_NO_ACTION;
15093     }
15094     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15095     {
15096       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15097         return MP_NO_ACTION;
15098     }
15099     else if (IS_EMC_GATE(element))
15100     {
15101       if (!player->key[EMC_GATE_NR(element)])
15102         return MP_NO_ACTION;
15103     }
15104     else if (IS_EMC_GATE_GRAY(element))
15105     {
15106       if (!player->key[EMC_GATE_GRAY_NR(element)])
15107         return MP_NO_ACTION;
15108     }
15109     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15110     {
15111       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15112         return MP_NO_ACTION;
15113     }
15114     else if (element == EL_DC_GATE_WHITE ||
15115              element == EL_DC_GATE_WHITE_GRAY ||
15116              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15117     {
15118       if (player->num_white_keys == 0)
15119         return MP_NO_ACTION;
15120
15121       player->num_white_keys--;
15122     }
15123     else if (IS_SP_PORT(element))
15124     {
15125       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15126           element == EL_SP_GRAVITY_PORT_RIGHT ||
15127           element == EL_SP_GRAVITY_PORT_UP ||
15128           element == EL_SP_GRAVITY_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130         player->gravity = !player->gravity;
15131 #else
15132         game.gravity = !game.gravity;
15133 #endif
15134       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15135                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15136                element == EL_SP_GRAVITY_ON_PORT_UP ||
15137                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139         player->gravity = TRUE;
15140 #else
15141         game.gravity = TRUE;
15142 #endif
15143       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15144                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15145                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15146                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15147 #if USE_PLAYER_GRAVITY
15148         player->gravity = FALSE;
15149 #else
15150         game.gravity = FALSE;
15151 #endif
15152     }
15153
15154     /* automatically move to the next field with double speed */
15155     player->programmed_action = move_direction;
15156
15157     if (player->move_delay_reset_counter == 0)
15158     {
15159       player->move_delay_reset_counter = 2;     /* two double speed steps */
15160
15161       DOUBLE_PLAYER_SPEED(player);
15162     }
15163
15164     PlayLevelSoundAction(x, y, ACTION_PASSING);
15165   }
15166   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15167   {
15168     RemoveField(x, y);
15169
15170     if (mode != DF_SNAP)
15171     {
15172       GfxElement[x][y] = GFX_ELEMENT(element);
15173       player->is_digging = TRUE;
15174     }
15175
15176     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15177
15178     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15179                                         player->index_bit, dig_side);
15180
15181     if (mode == DF_SNAP)
15182     {
15183 #if USE_NEW_SNAP_DELAY
15184       if (level.block_snap_field)
15185         setFieldForSnapping(x, y, element, move_direction);
15186       else
15187         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15188 #else
15189       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15190 #endif
15191
15192       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193                                           player->index_bit, dig_side);
15194     }
15195   }
15196   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15197   {
15198     RemoveField(x, y);
15199
15200     if (is_player && mode != DF_SNAP)
15201     {
15202       GfxElement[x][y] = element;
15203       player->is_collecting = TRUE;
15204     }
15205
15206     if (element == EL_SPEED_PILL)
15207     {
15208       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15209     }
15210     else if (element == EL_EXTRA_TIME && level.time > 0)
15211     {
15212       TimeLeft += level.extra_time;
15213
15214 #if 1
15215       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15216
15217       DisplayGameControlValues();
15218 #else
15219       DrawGameValue_Time(TimeLeft);
15220 #endif
15221     }
15222     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15223     {
15224       player->shield_normal_time_left += level.shield_normal_time;
15225       if (element == EL_SHIELD_DEADLY)
15226         player->shield_deadly_time_left += level.shield_deadly_time;
15227     }
15228     else if (element == EL_DYNAMITE ||
15229              element == EL_EM_DYNAMITE ||
15230              element == EL_SP_DISK_RED)
15231     {
15232       if (player->inventory_size < MAX_INVENTORY_SIZE)
15233         player->inventory_element[player->inventory_size++] = element;
15234
15235       DrawGameDoorValues();
15236     }
15237     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15238     {
15239       player->dynabomb_count++;
15240       player->dynabombs_left++;
15241     }
15242     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15243     {
15244       player->dynabomb_size++;
15245     }
15246     else if (element == EL_DYNABOMB_INCREASE_POWER)
15247     {
15248       player->dynabomb_xl = TRUE;
15249     }
15250     else if (IS_KEY(element))
15251     {
15252       player->key[KEY_NR(element)] = TRUE;
15253
15254       DrawGameDoorValues();
15255     }
15256     else if (element == EL_DC_KEY_WHITE)
15257     {
15258       player->num_white_keys++;
15259
15260       /* display white keys? */
15261       /* DrawGameDoorValues(); */
15262     }
15263     else if (IS_ENVELOPE(element))
15264     {
15265       player->show_envelope = element;
15266     }
15267     else if (element == EL_EMC_LENSES)
15268     {
15269       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15270
15271       RedrawAllInvisibleElementsForLenses();
15272     }
15273     else if (element == EL_EMC_MAGNIFIER)
15274     {
15275       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15276
15277       RedrawAllInvisibleElementsForMagnifier();
15278     }
15279     else if (IS_DROPPABLE(element) ||
15280              IS_THROWABLE(element))     /* can be collected and dropped */
15281     {
15282       int i;
15283
15284       if (collect_count == 0)
15285         player->inventory_infinite_element = element;
15286       else
15287         for (i = 0; i < collect_count; i++)
15288           if (player->inventory_size < MAX_INVENTORY_SIZE)
15289             player->inventory_element[player->inventory_size++] = element;
15290
15291       DrawGameDoorValues();
15292     }
15293     else if (collect_count > 0)
15294     {
15295       local_player->gems_still_needed -= collect_count;
15296       if (local_player->gems_still_needed < 0)
15297         local_player->gems_still_needed = 0;
15298
15299 #if 1
15300       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15301
15302       DisplayGameControlValues();
15303 #else
15304       DrawGameValue_Emeralds(local_player->gems_still_needed);
15305 #endif
15306     }
15307
15308     RaiseScoreElement(element);
15309     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15310
15311     if (is_player)
15312       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15313                                           player->index_bit, dig_side);
15314
15315     if (mode == DF_SNAP)
15316     {
15317 #if USE_NEW_SNAP_DELAY
15318       if (level.block_snap_field)
15319         setFieldForSnapping(x, y, element, move_direction);
15320       else
15321         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15322 #else
15323       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15324 #endif
15325
15326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327                                           player->index_bit, dig_side);
15328     }
15329   }
15330   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15331   {
15332     if (mode == DF_SNAP && element != EL_BD_ROCK)
15333       return MP_NO_ACTION;
15334
15335     if (CAN_FALL(element) && dy)
15336       return MP_NO_ACTION;
15337
15338     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15339         !(element == EL_SPRING && level.use_spring_bug))
15340       return MP_NO_ACTION;
15341
15342     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15343         ((move_direction & MV_VERTICAL &&
15344           ((element_info[element].move_pattern & MV_LEFT &&
15345             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15346            (element_info[element].move_pattern & MV_RIGHT &&
15347             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15348          (move_direction & MV_HORIZONTAL &&
15349           ((element_info[element].move_pattern & MV_UP &&
15350             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15351            (element_info[element].move_pattern & MV_DOWN &&
15352             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15353       return MP_NO_ACTION;
15354
15355     /* do not push elements already moving away faster than player */
15356     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15357         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15358       return MP_NO_ACTION;
15359
15360     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15361     {
15362       if (player->push_delay_value == -1 || !player_was_pushing)
15363         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15364     }
15365     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15366     {
15367       if (player->push_delay_value == -1)
15368         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15369     }
15370     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15371     {
15372       if (!player->is_pushing)
15373         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15374     }
15375
15376     player->is_pushing = TRUE;
15377     player->is_active = TRUE;
15378
15379     if (!(IN_LEV_FIELD(nextx, nexty) &&
15380           (IS_FREE(nextx, nexty) ||
15381            (IS_SB_ELEMENT(element) &&
15382             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15383            (IS_CUSTOM_ELEMENT(element) &&
15384             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15385       return MP_NO_ACTION;
15386
15387     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15388       return MP_NO_ACTION;
15389
15390     if (player->push_delay == -1)       /* new pushing; restart delay */
15391       player->push_delay = 0;
15392
15393     if (player->push_delay < player->push_delay_value &&
15394         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15395         element != EL_SPRING && element != EL_BALLOON)
15396     {
15397       /* make sure that there is no move delay before next try to push */
15398       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15399         player->move_delay = 0;
15400
15401       return MP_NO_ACTION;
15402     }
15403
15404     if (IS_CUSTOM_ELEMENT(element) &&
15405         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15406     {
15407       if (!DigFieldByCE(nextx, nexty, element))
15408         return MP_NO_ACTION;
15409     }
15410
15411     if (IS_SB_ELEMENT(element))
15412     {
15413       if (element == EL_SOKOBAN_FIELD_FULL)
15414       {
15415         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15416         local_player->sokobanfields_still_needed++;
15417       }
15418
15419       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15420       {
15421         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15422         local_player->sokobanfields_still_needed--;
15423       }
15424
15425       Feld[x][y] = EL_SOKOBAN_OBJECT;
15426
15427       if (Back[x][y] == Back[nextx][nexty])
15428         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15429       else if (Back[x][y] != 0)
15430         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15431                                     ACTION_EMPTYING);
15432       else
15433         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15434                                     ACTION_FILLING);
15435
15436 #if 1
15437       if (local_player->sokobanfields_still_needed == 0 &&
15438           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15439 #else
15440       if (local_player->sokobanfields_still_needed == 0 &&
15441           game.emulation == EMU_SOKOBAN)
15442 #endif
15443       {
15444         PlayerWins(player);
15445
15446         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15447       }
15448     }
15449     else
15450       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15451
15452     InitMovingField(x, y, move_direction);
15453     GfxAction[x][y] = ACTION_PUSHING;
15454
15455     if (mode == DF_SNAP)
15456       ContinueMoving(x, y);
15457     else
15458       MovPos[x][y] = (dx != 0 ? dx : dy);
15459
15460     Pushed[x][y] = TRUE;
15461     Pushed[nextx][nexty] = TRUE;
15462
15463     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15464       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15465     else
15466       player->push_delay_value = -1;    /* get new value later */
15467
15468     /* check for element change _after_ element has been pushed */
15469     if (game.use_change_when_pushing_bug)
15470     {
15471       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15472                                  player->index_bit, dig_side);
15473       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15474                                           player->index_bit, dig_side);
15475     }
15476   }
15477   else if (IS_SWITCHABLE(element))
15478   {
15479     if (PLAYER_SWITCHING(player, x, y))
15480     {
15481       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15482                                           player->index_bit, dig_side);
15483
15484       return MP_ACTION;
15485     }
15486
15487     player->is_switching = TRUE;
15488     player->switch_x = x;
15489     player->switch_y = y;
15490
15491     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15492
15493     if (element == EL_ROBOT_WHEEL)
15494     {
15495       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15496       ZX = x;
15497       ZY = y;
15498
15499       game.robot_wheel_active = TRUE;
15500
15501       TEST_DrawLevelField(x, y);
15502     }
15503     else if (element == EL_SP_TERMINAL)
15504     {
15505       int xx, yy;
15506
15507       SCAN_PLAYFIELD(xx, yy)
15508       {
15509         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15510           Bang(xx, yy);
15511         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15512           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15513       }
15514     }
15515     else if (IS_BELT_SWITCH(element))
15516     {
15517       ToggleBeltSwitch(x, y);
15518     }
15519     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15520              element == EL_SWITCHGATE_SWITCH_DOWN ||
15521              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15522              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15523     {
15524       ToggleSwitchgateSwitch(x, y);
15525     }
15526     else if (element == EL_LIGHT_SWITCH ||
15527              element == EL_LIGHT_SWITCH_ACTIVE)
15528     {
15529       ToggleLightSwitch(x, y);
15530     }
15531     else if (element == EL_TIMEGATE_SWITCH ||
15532              element == EL_DC_TIMEGATE_SWITCH)
15533     {
15534       ActivateTimegateSwitch(x, y);
15535     }
15536     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15537              element == EL_BALLOON_SWITCH_RIGHT ||
15538              element == EL_BALLOON_SWITCH_UP    ||
15539              element == EL_BALLOON_SWITCH_DOWN  ||
15540              element == EL_BALLOON_SWITCH_NONE  ||
15541              element == EL_BALLOON_SWITCH_ANY)
15542     {
15543       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15544                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15545                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15546                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15547                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15548                              move_direction);
15549     }
15550     else if (element == EL_LAMP)
15551     {
15552       Feld[x][y] = EL_LAMP_ACTIVE;
15553       local_player->lights_still_needed--;
15554
15555       ResetGfxAnimation(x, y);
15556       TEST_DrawLevelField(x, y);
15557     }
15558     else if (element == EL_TIME_ORB_FULL)
15559     {
15560       Feld[x][y] = EL_TIME_ORB_EMPTY;
15561
15562       if (level.time > 0 || level.use_time_orb_bug)
15563       {
15564         TimeLeft += level.time_orb_time;
15565
15566 #if 1
15567         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15568
15569         DisplayGameControlValues();
15570 #else
15571         DrawGameValue_Time(TimeLeft);
15572 #endif
15573       }
15574
15575       ResetGfxAnimation(x, y);
15576       TEST_DrawLevelField(x, y);
15577     }
15578     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15579              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15580     {
15581       int xx, yy;
15582
15583       game.ball_state = !game.ball_state;
15584
15585       SCAN_PLAYFIELD(xx, yy)
15586       {
15587         int e = Feld[xx][yy];
15588
15589         if (game.ball_state)
15590         {
15591           if (e == EL_EMC_MAGIC_BALL)
15592             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15593           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15594             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15595         }
15596         else
15597         {
15598           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15599             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15600           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15601             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15602         }
15603       }
15604     }
15605
15606     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15607                                         player->index_bit, dig_side);
15608
15609     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15610                                         player->index_bit, dig_side);
15611
15612     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15613                                         player->index_bit, dig_side);
15614
15615     return MP_ACTION;
15616   }
15617   else
15618   {
15619     if (!PLAYER_SWITCHING(player, x, y))
15620     {
15621       player->is_switching = TRUE;
15622       player->switch_x = x;
15623       player->switch_y = y;
15624
15625       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15626                                  player->index_bit, dig_side);
15627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15628                                           player->index_bit, dig_side);
15629
15630       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15631                                  player->index_bit, dig_side);
15632       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15633                                           player->index_bit, dig_side);
15634     }
15635
15636     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15637                                player->index_bit, dig_side);
15638     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15639                                         player->index_bit, dig_side);
15640
15641     return MP_NO_ACTION;
15642   }
15643
15644   player->push_delay = -1;
15645
15646   if (is_player)                /* function can also be called by EL_PENGUIN */
15647   {
15648     if (Feld[x][y] != element)          /* really digged/collected something */
15649     {
15650       player->is_collecting = !player->is_digging;
15651       player->is_active = TRUE;
15652     }
15653   }
15654
15655   return MP_MOVING;
15656 }
15657
15658 static boolean DigFieldByCE(int x, int y, int digging_element)
15659 {
15660   int element = Feld[x][y];
15661
15662   if (!IS_FREE(x, y))
15663   {
15664     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15665                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15666                   ACTION_BREAKING);
15667
15668     /* no element can dig solid indestructible elements */
15669     if (IS_INDESTRUCTIBLE(element) &&
15670         !IS_DIGGABLE(element) &&
15671         !IS_COLLECTIBLE(element))
15672       return FALSE;
15673
15674     if (AmoebaNr[x][y] &&
15675         (element == EL_AMOEBA_FULL ||
15676          element == EL_BD_AMOEBA ||
15677          element == EL_AMOEBA_GROWING))
15678     {
15679       AmoebaCnt[AmoebaNr[x][y]]--;
15680       AmoebaCnt2[AmoebaNr[x][y]]--;
15681     }
15682
15683     if (IS_MOVING(x, y))
15684       RemoveMovingField(x, y);
15685     else
15686     {
15687       RemoveField(x, y);
15688       TEST_DrawLevelField(x, y);
15689     }
15690
15691     /* if digged element was about to explode, prevent the explosion */
15692     ExplodeField[x][y] = EX_TYPE_NONE;
15693
15694     PlayLevelSoundAction(x, y, action);
15695   }
15696
15697   Store[x][y] = EL_EMPTY;
15698
15699 #if 1
15700   /* this makes it possible to leave the removed element again */
15701   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15702     Store[x][y] = element;
15703 #else
15704   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15705   {
15706     int move_leave_element = element_info[digging_element].move_leave_element;
15707
15708     /* this makes it possible to leave the removed element again */
15709     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15710                    element : move_leave_element);
15711   }
15712 #endif
15713
15714   return TRUE;
15715 }
15716
15717 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15718 {
15719   int jx = player->jx, jy = player->jy;
15720   int x = jx + dx, y = jy + dy;
15721   int snap_direction = (dx == -1 ? MV_LEFT  :
15722                         dx == +1 ? MV_RIGHT :
15723                         dy == -1 ? MV_UP    :
15724                         dy == +1 ? MV_DOWN  : MV_NONE);
15725   boolean can_continue_snapping = (level.continuous_snapping &&
15726                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15727
15728   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15729     return FALSE;
15730
15731   if (!player->active || !IN_LEV_FIELD(x, y))
15732     return FALSE;
15733
15734   if (dx && dy)
15735     return FALSE;
15736
15737   if (!dx && !dy)
15738   {
15739     if (player->MovPos == 0)
15740       player->is_pushing = FALSE;
15741
15742     player->is_snapping = FALSE;
15743
15744     if (player->MovPos == 0)
15745     {
15746       player->is_moving = FALSE;
15747       player->is_digging = FALSE;
15748       player->is_collecting = FALSE;
15749     }
15750
15751     return FALSE;
15752   }
15753
15754 #if USE_NEW_CONTINUOUS_SNAPPING
15755   /* prevent snapping with already pressed snap key when not allowed */
15756   if (player->is_snapping && !can_continue_snapping)
15757     return FALSE;
15758 #else
15759   if (player->is_snapping)
15760     return FALSE;
15761 #endif
15762
15763   player->MovDir = snap_direction;
15764
15765   if (player->MovPos == 0)
15766   {
15767     player->is_moving = FALSE;
15768     player->is_digging = FALSE;
15769     player->is_collecting = FALSE;
15770   }
15771
15772   player->is_dropping = FALSE;
15773   player->is_dropping_pressed = FALSE;
15774   player->drop_pressed_delay = 0;
15775
15776   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15777     return FALSE;
15778
15779   player->is_snapping = TRUE;
15780   player->is_active = TRUE;
15781
15782   if (player->MovPos == 0)
15783   {
15784     player->is_moving = FALSE;
15785     player->is_digging = FALSE;
15786     player->is_collecting = FALSE;
15787   }
15788
15789   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15790     TEST_DrawLevelField(player->last_jx, player->last_jy);
15791
15792   TEST_DrawLevelField(x, y);
15793
15794   return TRUE;
15795 }
15796
15797 static boolean DropElement(struct PlayerInfo *player)
15798 {
15799   int old_element, new_element;
15800   int dropx = player->jx, dropy = player->jy;
15801   int drop_direction = player->MovDir;
15802   int drop_side = drop_direction;
15803 #if 1
15804   int drop_element = get_next_dropped_element(player);
15805 #else
15806   int drop_element = (player->inventory_size > 0 ?
15807                       player->inventory_element[player->inventory_size - 1] :
15808                       player->inventory_infinite_element != EL_UNDEFINED ?
15809                       player->inventory_infinite_element :
15810                       player->dynabombs_left > 0 ?
15811                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15812                       EL_UNDEFINED);
15813 #endif
15814
15815   player->is_dropping_pressed = TRUE;
15816
15817   /* do not drop an element on top of another element; when holding drop key
15818      pressed without moving, dropped element must move away before the next
15819      element can be dropped (this is especially important if the next element
15820      is dynamite, which can be placed on background for historical reasons) */
15821   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15822     return MP_ACTION;
15823
15824   if (IS_THROWABLE(drop_element))
15825   {
15826     dropx += GET_DX_FROM_DIR(drop_direction);
15827     dropy += GET_DY_FROM_DIR(drop_direction);
15828
15829     if (!IN_LEV_FIELD(dropx, dropy))
15830       return FALSE;
15831   }
15832
15833   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15834   new_element = drop_element;           /* default: no change when dropping */
15835
15836   /* check if player is active, not moving and ready to drop */
15837   if (!player->active || player->MovPos || player->drop_delay > 0)
15838     return FALSE;
15839
15840   /* check if player has anything that can be dropped */
15841   if (new_element == EL_UNDEFINED)
15842     return FALSE;
15843
15844   /* check if drop key was pressed long enough for EM style dynamite */
15845   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15846     return FALSE;
15847
15848   /* check if anything can be dropped at the current position */
15849   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15850     return FALSE;
15851
15852   /* collected custom elements can only be dropped on empty fields */
15853   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15854     return FALSE;
15855
15856   if (old_element != EL_EMPTY)
15857     Back[dropx][dropy] = old_element;   /* store old element on this field */
15858
15859   ResetGfxAnimation(dropx, dropy);
15860   ResetRandomAnimationValue(dropx, dropy);
15861
15862   if (player->inventory_size > 0 ||
15863       player->inventory_infinite_element != EL_UNDEFINED)
15864   {
15865     if (player->inventory_size > 0)
15866     {
15867       player->inventory_size--;
15868
15869       DrawGameDoorValues();
15870
15871       if (new_element == EL_DYNAMITE)
15872         new_element = EL_DYNAMITE_ACTIVE;
15873       else if (new_element == EL_EM_DYNAMITE)
15874         new_element = EL_EM_DYNAMITE_ACTIVE;
15875       else if (new_element == EL_SP_DISK_RED)
15876         new_element = EL_SP_DISK_RED_ACTIVE;
15877     }
15878
15879     Feld[dropx][dropy] = new_element;
15880
15881     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15882       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15883                           el2img(Feld[dropx][dropy]), 0);
15884
15885     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15886
15887     /* needed if previous element just changed to "empty" in the last frame */
15888     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15889
15890     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15891                                player->index_bit, drop_side);
15892     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15893                                         CE_PLAYER_DROPS_X,
15894                                         player->index_bit, drop_side);
15895
15896     TestIfElementTouchesCustomElement(dropx, dropy);
15897   }
15898   else          /* player is dropping a dyna bomb */
15899   {
15900     player->dynabombs_left--;
15901
15902     Feld[dropx][dropy] = new_element;
15903
15904     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15905       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15906                           el2img(Feld[dropx][dropy]), 0);
15907
15908     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15909   }
15910
15911   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15912     InitField_WithBug1(dropx, dropy, FALSE);
15913
15914   new_element = Feld[dropx][dropy];     /* element might have changed */
15915
15916   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15917       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15918   {
15919     int move_direction, nextx, nexty;
15920
15921     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15922       MovDir[dropx][dropy] = drop_direction;
15923
15924     move_direction = MovDir[dropx][dropy];
15925     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15926     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15927
15928     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15929
15930 #if USE_FIX_IMPACT_COLLISION
15931     /* do not cause impact style collision by dropping elements that can fall */
15932     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15933 #else
15934     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15935 #endif
15936   }
15937
15938   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15939   player->is_dropping = TRUE;
15940
15941   player->drop_pressed_delay = 0;
15942   player->is_dropping_pressed = FALSE;
15943
15944   player->drop_x = dropx;
15945   player->drop_y = dropy;
15946
15947   return TRUE;
15948 }
15949
15950 /* ------------------------------------------------------------------------- */
15951 /* game sound playing functions                                              */
15952 /* ------------------------------------------------------------------------- */
15953
15954 static int *loop_sound_frame = NULL;
15955 static int *loop_sound_volume = NULL;
15956
15957 void InitPlayLevelSound()
15958 {
15959   int num_sounds = getSoundListSize();
15960
15961   checked_free(loop_sound_frame);
15962   checked_free(loop_sound_volume);
15963
15964   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15965   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15966 }
15967
15968 static void PlayLevelSound(int x, int y, int nr)
15969 {
15970   int sx = SCREENX(x), sy = SCREENY(y);
15971   int volume, stereo_position;
15972   int max_distance = 8;
15973   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15974
15975   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15976       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15977     return;
15978
15979   if (!IN_LEV_FIELD(x, y) ||
15980       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15981       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15982     return;
15983
15984   volume = SOUND_MAX_VOLUME;
15985
15986   if (!IN_SCR_FIELD(sx, sy))
15987   {
15988     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15989     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15990
15991     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15992   }
15993
15994   stereo_position = (SOUND_MAX_LEFT +
15995                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15996                      (SCR_FIELDX + 2 * max_distance));
15997
15998   if (IS_LOOP_SOUND(nr))
15999   {
16000     /* This assures that quieter loop sounds do not overwrite louder ones,
16001        while restarting sound volume comparison with each new game frame. */
16002
16003     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16004       return;
16005
16006     loop_sound_volume[nr] = volume;
16007     loop_sound_frame[nr] = FrameCounter;
16008   }
16009
16010   PlaySoundExt(nr, volume, stereo_position, type);
16011 }
16012
16013 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16014 {
16015   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16016                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16017                  y < LEVELY(BY1) ? LEVELY(BY1) :
16018                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16019                  sound_action);
16020 }
16021
16022 static void PlayLevelSoundAction(int x, int y, int action)
16023 {
16024   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16025 }
16026
16027 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16028 {
16029   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16030
16031   if (sound_effect != SND_UNDEFINED)
16032     PlayLevelSound(x, y, sound_effect);
16033 }
16034
16035 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16036                                               int action)
16037 {
16038   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16039
16040   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16041     PlayLevelSound(x, y, sound_effect);
16042 }
16043
16044 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16045 {
16046   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16047
16048   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16049     PlayLevelSound(x, y, sound_effect);
16050 }
16051
16052 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16053 {
16054   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16055
16056   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16057     StopSound(sound_effect);
16058 }
16059
16060 static void PlayLevelMusic()
16061 {
16062   if (levelset.music[level_nr] != MUS_UNDEFINED)
16063     PlayMusic(levelset.music[level_nr]);        /* from config file */
16064   else
16065     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16066 }
16067
16068 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16069 {
16070   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16071   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16072   int x = xx - 1 - offset;
16073   int y = yy - 1 - offset;
16074
16075   switch (sample)
16076   {
16077     case SAMPLE_blank:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16079       break;
16080
16081     case SAMPLE_roll:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16083       break;
16084
16085     case SAMPLE_stone:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16087       break;
16088
16089     case SAMPLE_nut:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16091       break;
16092
16093     case SAMPLE_crack:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16095       break;
16096
16097     case SAMPLE_bug:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16099       break;
16100
16101     case SAMPLE_tank:
16102       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16103       break;
16104
16105     case SAMPLE_android_clone:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16107       break;
16108
16109     case SAMPLE_android_move:
16110       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16111       break;
16112
16113     case SAMPLE_spring:
16114       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16115       break;
16116
16117     case SAMPLE_slurp:
16118       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16119       break;
16120
16121     case SAMPLE_eater:
16122       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16123       break;
16124
16125     case SAMPLE_eater_eat:
16126       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16127       break;
16128
16129     case SAMPLE_alien:
16130       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16131       break;
16132
16133     case SAMPLE_collect:
16134       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16135       break;
16136
16137     case SAMPLE_diamond:
16138       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16139       break;
16140
16141     case SAMPLE_squash:
16142       /* !!! CHECK THIS !!! */
16143 #if 1
16144       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16145 #else
16146       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16147 #endif
16148       break;
16149
16150     case SAMPLE_wonderfall:
16151       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16152       break;
16153
16154     case SAMPLE_drip:
16155       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16156       break;
16157
16158     case SAMPLE_push:
16159       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16160       break;
16161
16162     case SAMPLE_dirt:
16163       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16164       break;
16165
16166     case SAMPLE_acid:
16167       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16168       break;
16169
16170     case SAMPLE_ball:
16171       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16172       break;
16173
16174     case SAMPLE_grow:
16175       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16176       break;
16177
16178     case SAMPLE_wonder:
16179       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16180       break;
16181
16182     case SAMPLE_door:
16183       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16184       break;
16185
16186     case SAMPLE_exit_open:
16187       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16188       break;
16189
16190     case SAMPLE_exit_leave:
16191       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16192       break;
16193
16194     case SAMPLE_dynamite:
16195       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16196       break;
16197
16198     case SAMPLE_tick:
16199       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16200       break;
16201
16202     case SAMPLE_press:
16203       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16204       break;
16205
16206     case SAMPLE_wheel:
16207       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16208       break;
16209
16210     case SAMPLE_boom:
16211       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16212       break;
16213
16214     case SAMPLE_die:
16215       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16216       break;
16217
16218     case SAMPLE_time:
16219       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16220       break;
16221
16222     default:
16223       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16224       break;
16225   }
16226 }
16227
16228 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16229 {
16230   int element = map_element_SP_to_RND(element_sp);
16231   int action = map_action_SP_to_RND(action_sp);
16232   int offset = (setup.sp_show_border_elements ? 0 : 1);
16233   int x = xx - offset;
16234   int y = yy - offset;
16235
16236 #if 0
16237   printf("::: %d -> %d\n", element_sp, action_sp);
16238 #endif
16239
16240   PlayLevelSoundElementAction(x, y, element, action);
16241 }
16242
16243 #if 0
16244 void ChangeTime(int value)
16245 {
16246   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16247
16248   *time += value;
16249
16250   /* EMC game engine uses value from time counter of RND game engine */
16251   level.native_em_level->lev->time = *time;
16252
16253   DrawGameValue_Time(*time);
16254 }
16255
16256 void RaiseScore(int value)
16257 {
16258   /* EMC game engine and RND game engine have separate score counters */
16259   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16260                 &level.native_em_level->lev->score : &local_player->score);
16261
16262   *score += value;
16263
16264   DrawGameValue_Score(*score);
16265 }
16266 #endif
16267
16268 void RaiseScore(int value)
16269 {
16270   local_player->score += value;
16271
16272 #if 1
16273   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16274
16275   DisplayGameControlValues();
16276 #else
16277   DrawGameValue_Score(local_player->score);
16278 #endif
16279 }
16280
16281 void RaiseScoreElement(int element)
16282 {
16283   switch (element)
16284   {
16285     case EL_EMERALD:
16286     case EL_BD_DIAMOND:
16287     case EL_EMERALD_YELLOW:
16288     case EL_EMERALD_RED:
16289     case EL_EMERALD_PURPLE:
16290     case EL_SP_INFOTRON:
16291       RaiseScore(level.score[SC_EMERALD]);
16292       break;
16293     case EL_DIAMOND:
16294       RaiseScore(level.score[SC_DIAMOND]);
16295       break;
16296     case EL_CRYSTAL:
16297       RaiseScore(level.score[SC_CRYSTAL]);
16298       break;
16299     case EL_PEARL:
16300       RaiseScore(level.score[SC_PEARL]);
16301       break;
16302     case EL_BUG:
16303     case EL_BD_BUTTERFLY:
16304     case EL_SP_ELECTRON:
16305       RaiseScore(level.score[SC_BUG]);
16306       break;
16307     case EL_SPACESHIP:
16308     case EL_BD_FIREFLY:
16309     case EL_SP_SNIKSNAK:
16310       RaiseScore(level.score[SC_SPACESHIP]);
16311       break;
16312     case EL_YAMYAM:
16313     case EL_DARK_YAMYAM:
16314       RaiseScore(level.score[SC_YAMYAM]);
16315       break;
16316     case EL_ROBOT:
16317       RaiseScore(level.score[SC_ROBOT]);
16318       break;
16319     case EL_PACMAN:
16320       RaiseScore(level.score[SC_PACMAN]);
16321       break;
16322     case EL_NUT:
16323       RaiseScore(level.score[SC_NUT]);
16324       break;
16325     case EL_DYNAMITE:
16326     case EL_EM_DYNAMITE:
16327     case EL_SP_DISK_RED:
16328     case EL_DYNABOMB_INCREASE_NUMBER:
16329     case EL_DYNABOMB_INCREASE_SIZE:
16330     case EL_DYNABOMB_INCREASE_POWER:
16331       RaiseScore(level.score[SC_DYNAMITE]);
16332       break;
16333     case EL_SHIELD_NORMAL:
16334     case EL_SHIELD_DEADLY:
16335       RaiseScore(level.score[SC_SHIELD]);
16336       break;
16337     case EL_EXTRA_TIME:
16338       RaiseScore(level.extra_time_score);
16339       break;
16340     case EL_KEY_1:
16341     case EL_KEY_2:
16342     case EL_KEY_3:
16343     case EL_KEY_4:
16344     case EL_EM_KEY_1:
16345     case EL_EM_KEY_2:
16346     case EL_EM_KEY_3:
16347     case EL_EM_KEY_4:
16348     case EL_EMC_KEY_5:
16349     case EL_EMC_KEY_6:
16350     case EL_EMC_KEY_7:
16351     case EL_EMC_KEY_8:
16352     case EL_DC_KEY_WHITE:
16353       RaiseScore(level.score[SC_KEY]);
16354       break;
16355     default:
16356       RaiseScore(element_info[element].collect_score);
16357       break;
16358   }
16359 }
16360
16361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16362 {
16363   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16364   {
16365 #if defined(NETWORK_AVALIABLE)
16366     if (options.network)
16367       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16368     else
16369 #endif
16370     {
16371       if (quick_quit)
16372       {
16373 #if 1
16374
16375 #if 1
16376         FadeSkipNextFadeIn();
16377 #else
16378         fading = fading_none;
16379 #endif
16380
16381 #else
16382         OpenDoor(DOOR_CLOSE_1);
16383 #endif
16384
16385         game_status = GAME_MODE_MAIN;
16386
16387 #if 1
16388         DrawAndFadeInMainMenu(REDRAW_FIELD);
16389 #else
16390         DrawMainMenu();
16391 #endif
16392       }
16393       else
16394       {
16395 #if 0
16396         FadeOut(REDRAW_FIELD);
16397 #endif
16398
16399         game_status = GAME_MODE_MAIN;
16400
16401         DrawAndFadeInMainMenu(REDRAW_FIELD);
16402       }
16403     }
16404   }
16405   else          /* continue playing the game */
16406   {
16407     if (tape.playing && tape.deactivate_display)
16408       TapeDeactivateDisplayOff(TRUE);
16409
16410     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16411
16412     if (tape.playing && tape.deactivate_display)
16413       TapeDeactivateDisplayOn();
16414   }
16415 }
16416
16417 void RequestQuitGame(boolean ask_if_really_quit)
16418 {
16419   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16420   boolean skip_request = AllPlayersGone || quick_quit;
16421
16422   RequestQuitGameExt(skip_request, quick_quit,
16423                      "Do you really want to quit the game ?");
16424 }
16425
16426
16427 /* ------------------------------------------------------------------------- */
16428 /* random generator functions                                                */
16429 /* ------------------------------------------------------------------------- */
16430
16431 unsigned int InitEngineRandom_RND(long seed)
16432 {
16433   game.num_random_calls = 0;
16434
16435 #if 0
16436   unsigned int rnd_seed = InitEngineRandom(seed);
16437
16438   printf("::: START RND: %d\n", rnd_seed);
16439
16440   return rnd_seed;
16441 #else
16442
16443   return InitEngineRandom(seed);
16444
16445 #endif
16446
16447 }
16448
16449 unsigned int RND(int max)
16450 {
16451   if (max > 0)
16452   {
16453     game.num_random_calls++;
16454
16455     return GetEngineRandom(max);
16456   }
16457
16458   return 0;
16459 }
16460
16461
16462 /* ------------------------------------------------------------------------- */
16463 /* game engine snapshot handling functions                                   */
16464 /* ------------------------------------------------------------------------- */
16465
16466 struct EngineSnapshotInfo
16467 {
16468   /* runtime values for custom element collect score */
16469   int collect_score[NUM_CUSTOM_ELEMENTS];
16470
16471   /* runtime values for group element choice position */
16472   int choice_pos[NUM_GROUP_ELEMENTS];
16473
16474   /* runtime values for belt position animations */
16475   int belt_graphic[4][NUM_BELT_PARTS];
16476   int belt_anim_mode[4][NUM_BELT_PARTS];
16477 };
16478
16479 static struct EngineSnapshotInfo engine_snapshot_rnd;
16480 static char *snapshot_level_identifier = NULL;
16481 static int snapshot_level_nr = -1;
16482
16483 static void SaveEngineSnapshotValues_RND()
16484 {
16485   static int belt_base_active_element[4] =
16486   {
16487     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16488     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16489     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16490     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16491   };
16492   int i, j;
16493
16494   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16495   {
16496     int element = EL_CUSTOM_START + i;
16497
16498     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16499   }
16500
16501   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16502   {
16503     int element = EL_GROUP_START + i;
16504
16505     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16506   }
16507
16508   for (i = 0; i < 4; i++)
16509   {
16510     for (j = 0; j < NUM_BELT_PARTS; j++)
16511     {
16512       int element = belt_base_active_element[i] + j;
16513       int graphic = el2img(element);
16514       int anim_mode = graphic_info[graphic].anim_mode;
16515
16516       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16517       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16518     }
16519   }
16520 }
16521
16522 static void LoadEngineSnapshotValues_RND()
16523 {
16524   unsigned long num_random_calls = game.num_random_calls;
16525   int i, j;
16526
16527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16528   {
16529     int element = EL_CUSTOM_START + i;
16530
16531     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16532   }
16533
16534   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16535   {
16536     int element = EL_GROUP_START + i;
16537
16538     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16539   }
16540
16541   for (i = 0; i < 4; i++)
16542   {
16543     for (j = 0; j < NUM_BELT_PARTS; j++)
16544     {
16545       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16546       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16547
16548       graphic_info[graphic].anim_mode = anim_mode;
16549     }
16550   }
16551
16552   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16553   {
16554     InitRND(tape.random_seed);
16555     for (i = 0; i < num_random_calls; i++)
16556       RND(1);
16557   }
16558
16559   if (game.num_random_calls != num_random_calls)
16560   {
16561     Error(ERR_INFO, "number of random calls out of sync");
16562     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16563     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16564     Error(ERR_EXIT, "this should not happen -- please debug");
16565   }
16566 }
16567
16568 void SaveEngineSnapshot()
16569 {
16570   /* do not save snapshots from editor */
16571   if (level_editor_test_game)
16572     return;
16573
16574   /* free previous snapshot buffers, if needed */
16575   FreeEngineSnapshotBuffers();
16576
16577   /* copy some special values to a structure better suited for the snapshot */
16578
16579   SaveEngineSnapshotValues_RND();
16580   SaveEngineSnapshotValues_EM();
16581   SaveEngineSnapshotValues_SP();
16582
16583   /* save values stored in special snapshot structure */
16584
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16588
16589   /* save further RND engine values */
16590
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16594
16595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16599
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16605
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16609
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16611
16612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16613
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16616
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16635
16636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16638
16639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16642
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16645
16646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16651
16652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16654
16655   /* save level identification information */
16656
16657   setString(&snapshot_level_identifier, leveldir_current->identifier);
16658   snapshot_level_nr = level_nr;
16659
16660 #if 0
16661   ListNode *node = engine_snapshot_list_rnd;
16662   int num_bytes = 0;
16663
16664   while (node != NULL)
16665   {
16666     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16667
16668     node = node->next;
16669   }
16670
16671   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16672 #endif
16673 }
16674
16675 void LoadEngineSnapshot()
16676 {
16677   /* restore generically stored snapshot buffers */
16678
16679   LoadEngineSnapshotBuffers();
16680
16681   /* restore special values from snapshot structure */
16682
16683   LoadEngineSnapshotValues_RND();
16684   LoadEngineSnapshotValues_EM();
16685   LoadEngineSnapshotValues_SP();
16686 }
16687
16688 boolean CheckEngineSnapshot()
16689 {
16690   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16691           snapshot_level_nr == level_nr);
16692 }
16693
16694
16695 /* ---------- new game button stuff ---------------------------------------- */
16696
16697 /* graphic position values for game buttons */
16698 #define GAME_BUTTON_XSIZE       30
16699 #define GAME_BUTTON_YSIZE       30
16700 #define GAME_BUTTON_XPOS        5
16701 #define GAME_BUTTON_YPOS        215
16702 #define SOUND_BUTTON_XPOS       5
16703 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16704
16705 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16706 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16707 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16708 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16709 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16710 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16711
16712 static struct
16713 {
16714   int *x, *y;
16715   int gd_x, gd_y;
16716   int gadget_id;
16717   char *infotext;
16718 } gamebutton_info[NUM_GAME_BUTTONS] =
16719 {
16720 #if 1
16721   {
16722     &game.button.stop.x,        &game.button.stop.y,
16723     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16724     GAME_CTRL_ID_STOP,
16725     "stop game"
16726   },
16727   {
16728     &game.button.pause.x,       &game.button.pause.y,
16729     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16730     GAME_CTRL_ID_PAUSE,
16731     "pause game"
16732   },
16733   {
16734     &game.button.play.x,        &game.button.play.y,
16735     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16736     GAME_CTRL_ID_PLAY,
16737     "play game"
16738   },
16739   {
16740     &game.button.sound_music.x, &game.button.sound_music.y,
16741     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16742     SOUND_CTRL_ID_MUSIC,
16743     "background music on/off"
16744   },
16745   {
16746     &game.button.sound_loops.x, &game.button.sound_loops.y,
16747     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16748     SOUND_CTRL_ID_LOOPS,
16749     "sound loops on/off"
16750   },
16751   {
16752     &game.button.sound_simple.x,&game.button.sound_simple.y,
16753     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16754     SOUND_CTRL_ID_SIMPLE,
16755     "normal sounds on/off"
16756   }
16757 #else
16758   {
16759     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16760     GAME_CTRL_ID_STOP,
16761     "stop game"
16762   },
16763   {
16764     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16765     GAME_CTRL_ID_PAUSE,
16766     "pause game"
16767   },
16768   {
16769     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16770     GAME_CTRL_ID_PLAY,
16771     "play game"
16772   },
16773   {
16774     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16775     SOUND_CTRL_ID_MUSIC,
16776     "background music on/off"
16777   },
16778   {
16779     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16780     SOUND_CTRL_ID_LOOPS,
16781     "sound loops on/off"
16782   },
16783   {
16784     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16785     SOUND_CTRL_ID_SIMPLE,
16786     "normal sounds on/off"
16787   }
16788 #endif
16789 };
16790
16791 void CreateGameButtons()
16792 {
16793   int i;
16794
16795   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16796   {
16797     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16798     struct GadgetInfo *gi;
16799     int button_type;
16800     boolean checked;
16801     unsigned long event_mask;
16802     int x, y;
16803     int gd_xoffset, gd_yoffset;
16804     int gd_x1, gd_x2, gd_y1, gd_y2;
16805     int id = i;
16806
16807     x = DX + *gamebutton_info[i].x;
16808     y = DY + *gamebutton_info[i].y;
16809     gd_xoffset = gamebutton_info[i].gd_x;
16810     gd_yoffset = gamebutton_info[i].gd_y;
16811     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16812     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16813
16814     if (id == GAME_CTRL_ID_STOP ||
16815         id == GAME_CTRL_ID_PAUSE ||
16816         id == GAME_CTRL_ID_PLAY)
16817     {
16818       button_type = GD_TYPE_NORMAL_BUTTON;
16819       checked = FALSE;
16820       event_mask = GD_EVENT_RELEASED;
16821       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16822       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16823     }
16824     else
16825     {
16826       button_type = GD_TYPE_CHECK_BUTTON;
16827       checked =
16828         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16829          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16830          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16831       event_mask = GD_EVENT_PRESSED;
16832       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16833       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16834     }
16835
16836     gi = CreateGadget(GDI_CUSTOM_ID, id,
16837                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16838 #if 1
16839                       GDI_X, x,
16840                       GDI_Y, y,
16841 #else
16842                       GDI_X, DX + gd_xoffset,
16843                       GDI_Y, DY + gd_yoffset,
16844 #endif
16845                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16846                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16847                       GDI_TYPE, button_type,
16848                       GDI_STATE, GD_BUTTON_UNPRESSED,
16849                       GDI_CHECKED, checked,
16850                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16851                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16852                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16853                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16854                       GDI_DIRECT_DRAW, FALSE,
16855                       GDI_EVENT_MASK, event_mask,
16856                       GDI_CALLBACK_ACTION, HandleGameButtons,
16857                       GDI_END);
16858
16859     if (gi == NULL)
16860       Error(ERR_EXIT, "cannot create gadget");
16861
16862     game_gadget[id] = gi;
16863   }
16864 }
16865
16866 void FreeGameButtons()
16867 {
16868   int i;
16869
16870   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16871     FreeGadget(game_gadget[i]);
16872 }
16873
16874 static void MapGameButtons()
16875 {
16876   int i;
16877
16878   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879     MapGadget(game_gadget[i]);
16880 }
16881
16882 void UnmapGameButtons()
16883 {
16884   int i;
16885
16886   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887     UnmapGadget(game_gadget[i]);
16888 }
16889
16890 void RedrawGameButtons()
16891 {
16892   int i;
16893
16894   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895     RedrawGadget(game_gadget[i]);
16896 }
16897
16898 static void HandleGameButtonsExt(int id)
16899 {
16900   if (game_status != GAME_MODE_PLAYING)
16901     return;
16902
16903   switch (id)
16904   {
16905     case GAME_CTRL_ID_STOP:
16906       if (tape.playing)
16907         TapeStop();
16908       else
16909         RequestQuitGame(TRUE);
16910       break;
16911
16912     case GAME_CTRL_ID_PAUSE:
16913       if (options.network)
16914       {
16915 #if defined(NETWORK_AVALIABLE)
16916         if (tape.pausing)
16917           SendToServer_ContinuePlaying();
16918         else
16919           SendToServer_PausePlaying();
16920 #endif
16921       }
16922       else
16923         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16924       break;
16925
16926     case GAME_CTRL_ID_PLAY:
16927       if (tape.pausing)
16928       {
16929 #if defined(NETWORK_AVALIABLE)
16930         if (options.network)
16931           SendToServer_ContinuePlaying();
16932         else
16933 #endif
16934         {
16935           tape.pausing = FALSE;
16936           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16937         }
16938       }
16939       break;
16940
16941     case SOUND_CTRL_ID_MUSIC:
16942       if (setup.sound_music)
16943       { 
16944         setup.sound_music = FALSE;
16945
16946         FadeMusic();
16947       }
16948       else if (audio.music_available)
16949       { 
16950         setup.sound = setup.sound_music = TRUE;
16951
16952         SetAudioMode(setup.sound);
16953
16954         PlayLevelMusic();
16955       }
16956       break;
16957
16958     case SOUND_CTRL_ID_LOOPS:
16959       if (setup.sound_loops)
16960         setup.sound_loops = FALSE;
16961       else if (audio.loops_available)
16962       {
16963         setup.sound = setup.sound_loops = TRUE;
16964
16965         SetAudioMode(setup.sound);
16966       }
16967       break;
16968
16969     case SOUND_CTRL_ID_SIMPLE:
16970       if (setup.sound_simple)
16971         setup.sound_simple = FALSE;
16972       else if (audio.sound_available)
16973       {
16974         setup.sound = setup.sound_simple = TRUE;
16975
16976         SetAudioMode(setup.sound);
16977       }
16978       break;
16979
16980     default:
16981       break;
16982   }
16983 }
16984
16985 static void HandleGameButtons(struct GadgetInfo *gi)
16986 {
16987   HandleGameButtonsExt(gi->custom_id);
16988 }
16989
16990 void HandleSoundButtonKeys(Key key)
16991 {
16992 #if 1
16993   if (key == setup.shortcut.sound_simple)
16994     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16995   else if (key == setup.shortcut.sound_loops)
16996     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16997   else if (key == setup.shortcut.sound_music)
16998     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16999 #else
17000   if (key == setup.shortcut.sound_simple)
17001     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17002   else if (key == setup.shortcut.sound_loops)
17003     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17004   else if (key == setup.shortcut.sound_music)
17005     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17006 #endif
17007 }