rnd-20100401-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   InitGameEngine();
3724   InitGameControlValues();
3725
3726   /* don't play tapes over network */
3727   network_playing = (options.network && !tape.playing);
3728
3729   for (i = 0; i < MAX_PLAYERS; i++)
3730   {
3731     struct PlayerInfo *player = &stored_player[i];
3732
3733     player->index_nr = i;
3734     player->index_bit = (1 << i);
3735     player->element_nr = EL_PLAYER_1 + i;
3736
3737     player->present = FALSE;
3738     player->active = FALSE;
3739     player->mapped = FALSE;
3740
3741     player->killed = FALSE;
3742     player->reanimated = FALSE;
3743
3744     player->action = 0;
3745     player->effective_action = 0;
3746     player->programmed_action = 0;
3747
3748     player->score = 0;
3749     player->score_final = 0;
3750
3751     player->gems_still_needed = level.gems_needed;
3752     player->sokobanfields_still_needed = 0;
3753     player->lights_still_needed = 0;
3754     player->friends_still_needed = 0;
3755
3756     for (j = 0; j < MAX_NUM_KEYS; j++)
3757       player->key[j] = FALSE;
3758
3759     player->num_white_keys = 0;
3760
3761     player->dynabomb_count = 0;
3762     player->dynabomb_size = 1;
3763     player->dynabombs_left = 0;
3764     player->dynabomb_xl = FALSE;
3765
3766     player->MovDir = initial_move_dir;
3767     player->MovPos = 0;
3768     player->GfxPos = 0;
3769     player->GfxDir = initial_move_dir;
3770     player->GfxAction = ACTION_DEFAULT;
3771     player->Frame = 0;
3772     player->StepFrame = 0;
3773
3774     player->initial_element = player->element_nr;
3775     player->artwork_element =
3776       (level.use_artwork_element[i] ? level.artwork_element[i] :
3777        player->element_nr);
3778     player->use_murphy = FALSE;
3779
3780     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3781     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3782
3783     player->gravity = level.initial_player_gravity[i];
3784
3785     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3786
3787     player->actual_frame_counter = 0;
3788
3789     player->step_counter = 0;
3790
3791     player->last_move_dir = initial_move_dir;
3792
3793     player->is_active = FALSE;
3794
3795     player->is_waiting = FALSE;
3796     player->is_moving = FALSE;
3797     player->is_auto_moving = FALSE;
3798     player->is_digging = FALSE;
3799     player->is_snapping = FALSE;
3800     player->is_collecting = FALSE;
3801     player->is_pushing = FALSE;
3802     player->is_switching = FALSE;
3803     player->is_dropping = FALSE;
3804     player->is_dropping_pressed = FALSE;
3805
3806     player->is_bored = FALSE;
3807     player->is_sleeping = FALSE;
3808
3809     player->frame_counter_bored = -1;
3810     player->frame_counter_sleeping = -1;
3811
3812     player->anim_delay_counter = 0;
3813     player->post_delay_counter = 0;
3814
3815     player->dir_waiting = initial_move_dir;
3816     player->action_waiting = ACTION_DEFAULT;
3817     player->last_action_waiting = ACTION_DEFAULT;
3818     player->special_action_bored = ACTION_DEFAULT;
3819     player->special_action_sleeping = ACTION_DEFAULT;
3820
3821     player->switch_x = -1;
3822     player->switch_y = -1;
3823
3824     player->drop_x = -1;
3825     player->drop_y = -1;
3826
3827     player->show_envelope = 0;
3828
3829     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3830
3831     player->push_delay       = -1;      /* initialized when pushing starts */
3832     player->push_delay_value = game.initial_push_delay_value;
3833
3834     player->drop_delay = 0;
3835     player->drop_pressed_delay = 0;
3836
3837     player->last_jx = -1;
3838     player->last_jy = -1;
3839     player->jx = -1;
3840     player->jy = -1;
3841
3842     player->shield_normal_time_left = 0;
3843     player->shield_deadly_time_left = 0;
3844
3845     player->inventory_infinite_element = EL_UNDEFINED;
3846     player->inventory_size = 0;
3847
3848     if (level.use_initial_inventory[i])
3849     {
3850       for (j = 0; j < level.initial_inventory_size[i]; j++)
3851       {
3852         int element = level.initial_inventory_content[i][j];
3853         int collect_count = element_info[element].collect_count_initial;
3854         int k;
3855
3856         if (!IS_CUSTOM_ELEMENT(element))
3857           collect_count = 1;
3858
3859         if (collect_count == 0)
3860           player->inventory_infinite_element = element;
3861         else
3862           for (k = 0; k < collect_count; k++)
3863             if (player->inventory_size < MAX_INVENTORY_SIZE)
3864               player->inventory_element[player->inventory_size++] = element;
3865       }
3866     }
3867
3868     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3869     SnapField(player, 0, 0);
3870
3871     player->LevelSolved = FALSE;
3872     player->GameOver = FALSE;
3873
3874     player->LevelSolved_GameWon = FALSE;
3875     player->LevelSolved_GameEnd = FALSE;
3876     player->LevelSolved_PanelOff = FALSE;
3877     player->LevelSolved_SaveTape = FALSE;
3878     player->LevelSolved_SaveScore = FALSE;
3879     player->LevelSolved_CountingTime = 0;
3880     player->LevelSolved_CountingScore = 0;
3881
3882     map_player_action[i] = i;
3883   }
3884
3885   network_player_action_received = FALSE;
3886
3887 #if defined(NETWORK_AVALIABLE)
3888   /* initial null action */
3889   if (network_playing)
3890     SendToServer_MovePlayer(MV_NONE);
3891 #endif
3892
3893   ZX = ZY = -1;
3894   ExitX = ExitY = -1;
3895
3896   FrameCounter = 0;
3897   TimeFrames = 0;
3898   TimePlayed = 0;
3899   TimeLeft = level.time;
3900   TapeTime = 0;
3901
3902   ScreenMovDir = MV_NONE;
3903   ScreenMovPos = 0;
3904   ScreenGfxPos = 0;
3905
3906   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3907
3908   AllPlayersGone = FALSE;
3909
3910   game.yamyam_content_nr = 0;
3911   game.robot_wheel_active = FALSE;
3912   game.magic_wall_active = FALSE;
3913   game.magic_wall_time_left = 0;
3914   game.light_time_left = 0;
3915   game.timegate_time_left = 0;
3916   game.switchgate_pos = 0;
3917   game.wind_direction = level.wind_direction_initial;
3918
3919 #if !USE_PLAYER_GRAVITY
3920   game.gravity = FALSE;
3921   game.explosions_delayed = TRUE;
3922 #endif
3923
3924   game.lenses_time_left = 0;
3925   game.magnify_time_left = 0;
3926
3927   game.ball_state = level.ball_state_initial;
3928   game.ball_content_nr = 0;
3929
3930   game.envelope_active = FALSE;
3931
3932   /* set focus to local player for network games, else to all players */
3933   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3934   game.centered_player_nr_next = game.centered_player_nr;
3935   game.set_centered_player = FALSE;
3936
3937   if (network_playing && tape.recording)
3938   {
3939     /* store client dependent player focus when recording network games */
3940     tape.centered_player_nr_next = game.centered_player_nr_next;
3941     tape.set_centered_player = TRUE;
3942   }
3943
3944   for (i = 0; i < NUM_BELTS; i++)
3945   {
3946     game.belt_dir[i] = MV_NONE;
3947     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3948   }
3949
3950   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3951     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3952
3953   SCAN_PLAYFIELD(x, y)
3954   {
3955     Feld[x][y] = level.field[x][y];
3956     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3957     ChangeDelay[x][y] = 0;
3958     ChangePage[x][y] = -1;
3959 #if USE_NEW_CUSTOM_VALUE
3960     CustomValue[x][y] = 0;              /* initialized in InitField() */
3961 #endif
3962     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3963     AmoebaNr[x][y] = 0;
3964     WasJustMoving[x][y] = 0;
3965     WasJustFalling[x][y] = 0;
3966     CheckCollision[x][y] = 0;
3967     CheckImpact[x][y] = 0;
3968     Stop[x][y] = FALSE;
3969     Pushed[x][y] = FALSE;
3970
3971     ChangeCount[x][y] = 0;
3972     ChangeEvent[x][y] = -1;
3973
3974     ExplodePhase[x][y] = 0;
3975     ExplodeDelay[x][y] = 0;
3976     ExplodeField[x][y] = EX_TYPE_NONE;
3977
3978     RunnerVisit[x][y] = 0;
3979     PlayerVisit[x][y] = 0;
3980
3981     GfxFrame[x][y] = 0;
3982     GfxRandom[x][y] = INIT_GFX_RANDOM();
3983     GfxElement[x][y] = EL_UNDEFINED;
3984     GfxAction[x][y] = ACTION_DEFAULT;
3985     GfxDir[x][y] = MV_NONE;
3986     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3987   }
3988
3989   SCAN_PLAYFIELD(x, y)
3990   {
3991     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3992       emulate_bd = FALSE;
3993     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3994       emulate_sb = FALSE;
3995     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3996       emulate_sp = FALSE;
3997
3998     InitField(x, y, TRUE);
3999
4000     ResetGfxAnimation(x, y);
4001   }
4002
4003   InitBeltMovement();
4004
4005   for (i = 0; i < MAX_PLAYERS; i++)
4006   {
4007     struct PlayerInfo *player = &stored_player[i];
4008
4009     /* set number of special actions for bored and sleeping animation */
4010     player->num_special_action_bored =
4011       get_num_special_action(player->artwork_element,
4012                              ACTION_BORING_1, ACTION_BORING_LAST);
4013     player->num_special_action_sleeping =
4014       get_num_special_action(player->artwork_element,
4015                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4016   }
4017
4018   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4019                     emulate_sb ? EMU_SOKOBAN :
4020                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4021
4022 #if USE_NEW_ALL_SLIPPERY
4023   /* initialize type of slippery elements */
4024   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4025   {
4026     if (!IS_CUSTOM_ELEMENT(i))
4027     {
4028       /* default: elements slip down either to the left or right randomly */
4029       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4030
4031       /* SP style elements prefer to slip down on the left side */
4032       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4033         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4034
4035       /* BD style elements prefer to slip down on the left side */
4036       if (game.emulation == EMU_BOULDERDASH)
4037         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4038     }
4039   }
4040 #endif
4041
4042   /* initialize explosion and ignition delay */
4043   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4044   {
4045     if (!IS_CUSTOM_ELEMENT(i))
4046     {
4047       int num_phase = 8;
4048       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4049                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4050                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4051       int last_phase = (num_phase + 1) * delay;
4052       int half_phase = (num_phase / 2) * delay;
4053
4054       element_info[i].explosion_delay = last_phase - 1;
4055       element_info[i].ignition_delay = half_phase;
4056
4057       if (i == EL_BLACK_ORB)
4058         element_info[i].ignition_delay = 1;
4059     }
4060
4061 #if 0
4062     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4063       element_info[i].explosion_delay = 1;
4064
4065     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4066       element_info[i].ignition_delay = 1;
4067 #endif
4068   }
4069
4070   /* correct non-moving belts to start moving left */
4071   for (i = 0; i < NUM_BELTS; i++)
4072     if (game.belt_dir[i] == MV_NONE)
4073       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4074
4075 #if USE_NEW_PLAYER_ASSIGNMENTS
4076   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4077   /* choose default local player */
4078   local_player = &stored_player[0];
4079
4080   for (i = 0; i < MAX_PLAYERS; i++)
4081     stored_player[i].connected = FALSE;
4082
4083   local_player->connected = TRUE;
4084   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4085
4086   if (tape.playing)
4087   {
4088     /* try to guess locally connected team mode players (needed for correct
4089        assignment of player figures from level to locally playing players) */
4090
4091     for (i = 0; i < MAX_PLAYERS; i++)
4092       if (tape.player_participates[i])
4093         stored_player[i].connected = TRUE;
4094   }
4095   else if (setup.team_mode && !options.network)
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 (setup.input[i].use_joystick ||
4102           setup.input[i].key.left != KSYM_UNDEFINED)
4103         stored_player[i].connected = TRUE;
4104   }
4105
4106 #if 0
4107   for (i = 0; i < MAX_PLAYERS; i++)
4108     printf("::: player %d: %s\n", i,
4109            (stored_player[i].connected ? "connected" : "not connected"));
4110
4111   for (i = 0; i < MAX_PLAYERS; i++)
4112     printf("::: player %d: %s\n", i,
4113            (stored_player[i].present ? "present" : "not present"));
4114 #endif
4115
4116   /* check if any connected player was not found in playfield */
4117   for (i = 0; i < MAX_PLAYERS; i++)
4118   {
4119     struct PlayerInfo *player = &stored_player[i];
4120
4121     if (player->connected && !player->present)
4122     {
4123       struct PlayerInfo *field_player = NULL;
4124
4125 #if 0
4126       printf("::: looking for field player for player %d ...\n", i);
4127 #endif
4128
4129       /* assign first free player found that is present in the playfield */
4130
4131       /* first try: look for unmapped playfield player that is not connected */
4132       if (field_player == NULL)
4133         for (j = 0; j < MAX_PLAYERS; j++)
4134           if (stored_player[j].present &&
4135               !stored_player[j].mapped &&
4136               !stored_player[j].connected)
4137             field_player = &stored_player[j];
4138
4139       /* second try: look for *any* unmapped playfield player */
4140       if (field_player == NULL)
4141         for (j = 0; j < MAX_PLAYERS; j++)
4142           if (stored_player[j].present &&
4143               !stored_player[j].mapped)
4144             field_player = &stored_player[j];
4145
4146       if (field_player != NULL)
4147       {
4148         int jx = field_player->jx, jy = field_player->jy;
4149
4150 #if 0
4151         printf("::: found player figure %d\n", field_player->index_nr);
4152 #endif
4153
4154         player->present = FALSE;
4155         player->active = FALSE;
4156
4157         field_player->present = TRUE;
4158         field_player->active = TRUE;
4159
4160         /*
4161         player->initial_element = field_player->initial_element;
4162         player->artwork_element = field_player->artwork_element;
4163
4164         player->block_last_field       = field_player->block_last_field;
4165         player->block_delay_adjustment = field_player->block_delay_adjustment;
4166         */
4167
4168         StorePlayer[jx][jy] = field_player->element_nr;
4169
4170         field_player->jx = field_player->last_jx = jx;
4171         field_player->jy = field_player->last_jy = jy;
4172
4173         if (local_player == player)
4174           local_player = field_player;
4175
4176         map_player_action[field_player->index_nr] = i;
4177
4178         field_player->mapped = TRUE;
4179
4180 #if 0
4181         printf("::: map_player_action[%d] == %d\n",
4182                field_player->index_nr, i);
4183 #endif
4184       }
4185     }
4186
4187     if (player->connected && player->present)
4188       player->mapped = TRUE;
4189   }
4190
4191 #else
4192
4193   /* check if any connected player was not found in playfield */
4194   for (i = 0; i < MAX_PLAYERS; i++)
4195   {
4196     struct PlayerInfo *player = &stored_player[i];
4197
4198     if (player->connected && !player->present)
4199     {
4200       for (j = 0; j < MAX_PLAYERS; j++)
4201       {
4202         struct PlayerInfo *field_player = &stored_player[j];
4203         int jx = field_player->jx, jy = field_player->jy;
4204
4205         /* assign first free player found that is present in the playfield */
4206         if (field_player->present && !field_player->connected)
4207         {
4208           player->present = TRUE;
4209           player->active = TRUE;
4210
4211           field_player->present = FALSE;
4212           field_player->active = FALSE;
4213
4214           player->initial_element = field_player->initial_element;
4215           player->artwork_element = field_player->artwork_element;
4216
4217           player->block_last_field       = field_player->block_last_field;
4218           player->block_delay_adjustment = field_player->block_delay_adjustment;
4219
4220           StorePlayer[jx][jy] = player->element_nr;
4221
4222           player->jx = player->last_jx = jx;
4223           player->jy = player->last_jy = jy;
4224
4225           break;
4226         }
4227       }
4228     }
4229   }
4230 #endif
4231
4232 #if 0
4233   printf("::: local_player->present == %d\n", local_player->present);
4234 #endif
4235
4236   if (tape.playing)
4237   {
4238     /* when playing a tape, eliminate all players who do not participate */
4239
4240 #if USE_NEW_PLAYER_ASSIGNMENTS
4241     for (i = 0; i < MAX_PLAYERS; i++)
4242     {
4243       if (stored_player[i].active &&
4244           !tape.player_participates[map_player_action[i]])
4245       {
4246         struct PlayerInfo *player = &stored_player[i];
4247         int jx = player->jx, jy = player->jy;
4248
4249         player->active = FALSE;
4250         StorePlayer[jx][jy] = 0;
4251         Feld[jx][jy] = EL_EMPTY;
4252       }
4253     }
4254 #else
4255     for (i = 0; i < MAX_PLAYERS; i++)
4256     {
4257       if (stored_player[i].active &&
4258           !tape.player_participates[i])
4259       {
4260         struct PlayerInfo *player = &stored_player[i];
4261         int jx = player->jx, jy = player->jy;
4262
4263         player->active = FALSE;
4264         StorePlayer[jx][jy] = 0;
4265         Feld[jx][jy] = EL_EMPTY;
4266       }
4267     }
4268 #endif
4269   }
4270   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4271   {
4272     /* when in single player mode, eliminate all but the first active player */
4273
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275     {
4276       if (stored_player[i].active)
4277       {
4278         for (j = i + 1; j < MAX_PLAYERS; j++)
4279         {
4280           if (stored_player[j].active)
4281           {
4282             struct PlayerInfo *player = &stored_player[j];
4283             int jx = player->jx, jy = player->jy;
4284
4285             player->active = FALSE;
4286             player->present = FALSE;
4287
4288             StorePlayer[jx][jy] = 0;
4289             Feld[jx][jy] = EL_EMPTY;
4290           }
4291         }
4292       }
4293     }
4294   }
4295
4296   /* when recording the game, store which players take part in the game */
4297   if (tape.recording)
4298   {
4299 #if USE_NEW_PLAYER_ASSIGNMENTS
4300     for (i = 0; i < MAX_PLAYERS; i++)
4301       if (stored_player[i].connected)
4302         tape.player_participates[i] = TRUE;
4303 #else
4304     for (i = 0; i < MAX_PLAYERS; i++)
4305       if (stored_player[i].active)
4306         tape.player_participates[i] = TRUE;
4307 #endif
4308   }
4309
4310   if (options.debug)
4311   {
4312     for (i = 0; i < MAX_PLAYERS; i++)
4313     {
4314       struct PlayerInfo *player = &stored_player[i];
4315
4316       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4317              i+1,
4318              player->present,
4319              player->connected,
4320              player->active);
4321       if (local_player == player)
4322         printf("Player  %d is local player.\n", i+1);
4323     }
4324   }
4325
4326   if (BorderElement == EL_EMPTY)
4327   {
4328     SBX_Left = 0;
4329     SBX_Right = lev_fieldx - SCR_FIELDX;
4330     SBY_Upper = 0;
4331     SBY_Lower = lev_fieldy - SCR_FIELDY;
4332   }
4333   else
4334   {
4335     SBX_Left = -1;
4336     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4337     SBY_Upper = -1;
4338     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4339   }
4340
4341   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4342     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4343
4344   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4345     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4346
4347   /* if local player not found, look for custom element that might create
4348      the player (make some assumptions about the right custom element) */
4349   if (!local_player->present)
4350   {
4351     int start_x = 0, start_y = 0;
4352     int found_rating = 0;
4353     int found_element = EL_UNDEFINED;
4354     int player_nr = local_player->index_nr;
4355
4356     SCAN_PLAYFIELD(x, y)
4357     {
4358       int element = Feld[x][y];
4359       int content;
4360       int xx, yy;
4361       boolean is_player;
4362
4363       if (level.use_start_element[player_nr] &&
4364           level.start_element[player_nr] == element &&
4365           found_rating < 4)
4366       {
4367         start_x = x;
4368         start_y = y;
4369
4370         found_rating = 4;
4371         found_element = element;
4372       }
4373
4374       if (!IS_CUSTOM_ELEMENT(element))
4375         continue;
4376
4377       if (CAN_CHANGE(element))
4378       {
4379         for (i = 0; i < element_info[element].num_change_pages; i++)
4380         {
4381           /* check for player created from custom element as single target */
4382           content = element_info[element].change_page[i].target_element;
4383           is_player = ELEM_IS_PLAYER(content);
4384
4385           if (is_player && (found_rating < 3 ||
4386                             (found_rating == 3 && element < found_element)))
4387           {
4388             start_x = x;
4389             start_y = y;
4390
4391             found_rating = 3;
4392             found_element = element;
4393           }
4394         }
4395       }
4396
4397       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4398       {
4399         /* check for player created from custom element as explosion content */
4400         content = element_info[element].content.e[xx][yy];
4401         is_player = ELEM_IS_PLAYER(content);
4402
4403         if (is_player && (found_rating < 2 ||
4404                           (found_rating == 2 && element < found_element)))
4405         {
4406           start_x = x + xx - 1;
4407           start_y = y + yy - 1;
4408
4409           found_rating = 2;
4410           found_element = element;
4411         }
4412
4413         if (!CAN_CHANGE(element))
4414           continue;
4415
4416         for (i = 0; i < element_info[element].num_change_pages; i++)
4417         {
4418           /* check for player created from custom element as extended target */
4419           content =
4420             element_info[element].change_page[i].target_content.e[xx][yy];
4421
4422           is_player = ELEM_IS_PLAYER(content);
4423
4424           if (is_player && (found_rating < 1 ||
4425                             (found_rating == 1 && element < found_element)))
4426           {
4427             start_x = x + xx - 1;
4428             start_y = y + yy - 1;
4429
4430             found_rating = 1;
4431             found_element = element;
4432           }
4433         }
4434       }
4435     }
4436
4437     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4438                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4439                 start_x - MIDPOSX);
4440
4441     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4442                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4443                 start_y - MIDPOSY);
4444   }
4445   else
4446   {
4447     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4448                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4449                 local_player->jx - MIDPOSX);
4450
4451     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4452                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4453                 local_player->jy - MIDPOSY);
4454   }
4455
4456 #if 0
4457   /* do not use PLAYING mask for fading out from main screen */
4458   game_status = GAME_MODE_MAIN;
4459 #endif
4460
4461   StopAnimation();
4462
4463   if (!game.restart_level)
4464     CloseDoor(DOOR_CLOSE_1);
4465
4466 #if 1
4467   if (level_editor_test_game)
4468     FadeSkipNextFadeIn();
4469   else
4470     FadeSetEnterScreen();
4471 #else
4472   if (level_editor_test_game)
4473     fading = fading_none;
4474   else
4475     fading = menu.destination;
4476 #endif
4477
4478 #if 1
4479   FadeOut(REDRAW_FIELD);
4480 #else
4481   if (do_fading)
4482     FadeOut(REDRAW_FIELD);
4483 #endif
4484
4485 #if 0
4486   game_status = GAME_MODE_PLAYING;
4487 #endif
4488
4489   /* !!! FIX THIS (START) !!! */
4490   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4491   {
4492     InitGameEngine_EM();
4493
4494     /* blit playfield from scroll buffer to normal back buffer for fading in */
4495     BlitScreenToBitmap_EM(backbuffer);
4496   }
4497   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4498   {
4499     InitGameEngine_SP();
4500
4501     /* blit playfield from scroll buffer to normal back buffer for fading in */
4502     BlitScreenToBitmap_SP(backbuffer);
4503   }
4504   else
4505   {
4506     DrawLevel();
4507     DrawAllPlayers();
4508
4509     /* after drawing the level, correct some elements */
4510     if (game.timegate_time_left == 0)
4511       CloseAllOpenTimegates();
4512
4513     /* blit playfield from scroll buffer to normal back buffer for fading in */
4514     if (setup.soft_scrolling)
4515       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4516
4517     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4518   }
4519   /* !!! FIX THIS (END) !!! */
4520
4521 #if 1
4522   FadeIn(REDRAW_FIELD);
4523 #else
4524   if (do_fading)
4525     FadeIn(REDRAW_FIELD);
4526
4527   BackToFront();
4528 #endif
4529
4530   if (!game.restart_level)
4531   {
4532     /* copy default game door content to main double buffer */
4533     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4534                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4535   }
4536
4537   SetPanelBackground();
4538   SetDrawBackgroundMask(REDRAW_DOOR_1);
4539
4540 #if 1
4541   UpdateAndDisplayGameControlValues();
4542 #else
4543   UpdateGameDoorValues();
4544   DrawGameDoorValues();
4545 #endif
4546
4547   if (!game.restart_level)
4548   {
4549     UnmapGameButtons();
4550     UnmapTapeButtons();
4551     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4552     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4553     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4554     MapGameButtons();
4555     MapTapeButtons();
4556
4557     /* copy actual game door content to door double buffer for OpenDoor() */
4558     BlitBitmap(drawto, bitmap_db_door,
4559                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4560
4561     OpenDoor(DOOR_OPEN_ALL);
4562
4563     PlaySound(SND_GAME_STARTING);
4564
4565     if (setup.sound_music)
4566       PlayLevelMusic();
4567
4568     KeyboardAutoRepeatOffUnlessAutoplay();
4569
4570     if (options.debug)
4571     {
4572       for (i = 0; i < MAX_PLAYERS; i++)
4573         printf("Player %d %sactive.\n",
4574                i + 1, (stored_player[i].active ? "" : "not "));
4575     }
4576   }
4577
4578 #if 1
4579   UnmapAllGadgets();
4580
4581   MapGameButtons();
4582   MapTapeButtons();
4583 #endif
4584
4585   game.restart_level = FALSE;
4586 }
4587
4588 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4589 {
4590   /* this is used for non-R'n'D game engines to update certain engine values */
4591
4592   /* needed to determine if sounds are played within the visible screen area */
4593   scroll_x = actual_scroll_x;
4594   scroll_y = actual_scroll_y;
4595 }
4596
4597 void InitMovDir(int x, int y)
4598 {
4599   int i, element = Feld[x][y];
4600   static int xy[4][2] =
4601   {
4602     {  0, +1 },
4603     { +1,  0 },
4604     {  0, -1 },
4605     { -1,  0 }
4606   };
4607   static int direction[3][4] =
4608   {
4609     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4610     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4611     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4612   };
4613
4614   switch (element)
4615   {
4616     case EL_BUG_RIGHT:
4617     case EL_BUG_UP:
4618     case EL_BUG_LEFT:
4619     case EL_BUG_DOWN:
4620       Feld[x][y] = EL_BUG;
4621       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4622       break;
4623
4624     case EL_SPACESHIP_RIGHT:
4625     case EL_SPACESHIP_UP:
4626     case EL_SPACESHIP_LEFT:
4627     case EL_SPACESHIP_DOWN:
4628       Feld[x][y] = EL_SPACESHIP;
4629       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4630       break;
4631
4632     case EL_BD_BUTTERFLY_RIGHT:
4633     case EL_BD_BUTTERFLY_UP:
4634     case EL_BD_BUTTERFLY_LEFT:
4635     case EL_BD_BUTTERFLY_DOWN:
4636       Feld[x][y] = EL_BD_BUTTERFLY;
4637       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4638       break;
4639
4640     case EL_BD_FIREFLY_RIGHT:
4641     case EL_BD_FIREFLY_UP:
4642     case EL_BD_FIREFLY_LEFT:
4643     case EL_BD_FIREFLY_DOWN:
4644       Feld[x][y] = EL_BD_FIREFLY;
4645       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4646       break;
4647
4648     case EL_PACMAN_RIGHT:
4649     case EL_PACMAN_UP:
4650     case EL_PACMAN_LEFT:
4651     case EL_PACMAN_DOWN:
4652       Feld[x][y] = EL_PACMAN;
4653       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4654       break;
4655
4656     case EL_YAMYAM_LEFT:
4657     case EL_YAMYAM_RIGHT:
4658     case EL_YAMYAM_UP:
4659     case EL_YAMYAM_DOWN:
4660       Feld[x][y] = EL_YAMYAM;
4661       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4662       break;
4663
4664     case EL_SP_SNIKSNAK:
4665       MovDir[x][y] = MV_UP;
4666       break;
4667
4668     case EL_SP_ELECTRON:
4669       MovDir[x][y] = MV_LEFT;
4670       break;
4671
4672     case EL_MOLE_LEFT:
4673     case EL_MOLE_RIGHT:
4674     case EL_MOLE_UP:
4675     case EL_MOLE_DOWN:
4676       Feld[x][y] = EL_MOLE;
4677       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4678       break;
4679
4680     default:
4681       if (IS_CUSTOM_ELEMENT(element))
4682       {
4683         struct ElementInfo *ei = &element_info[element];
4684         int move_direction_initial = ei->move_direction_initial;
4685         int move_pattern = ei->move_pattern;
4686
4687         if (move_direction_initial == MV_START_PREVIOUS)
4688         {
4689           if (MovDir[x][y] != MV_NONE)
4690             return;
4691
4692           move_direction_initial = MV_START_AUTOMATIC;
4693         }
4694
4695         if (move_direction_initial == MV_START_RANDOM)
4696           MovDir[x][y] = 1 << RND(4);
4697         else if (move_direction_initial & MV_ANY_DIRECTION)
4698           MovDir[x][y] = move_direction_initial;
4699         else if (move_pattern == MV_ALL_DIRECTIONS ||
4700                  move_pattern == MV_TURNING_LEFT ||
4701                  move_pattern == MV_TURNING_RIGHT ||
4702                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4703                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4704                  move_pattern == MV_TURNING_RANDOM)
4705           MovDir[x][y] = 1 << RND(4);
4706         else if (move_pattern == MV_HORIZONTAL)
4707           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4708         else if (move_pattern == MV_VERTICAL)
4709           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4710         else if (move_pattern & MV_ANY_DIRECTION)
4711           MovDir[x][y] = element_info[element].move_pattern;
4712         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4713                  move_pattern == MV_ALONG_RIGHT_SIDE)
4714         {
4715           /* use random direction as default start direction */
4716           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4717             MovDir[x][y] = 1 << RND(4);
4718
4719           for (i = 0; i < NUM_DIRECTIONS; i++)
4720           {
4721             int x1 = x + xy[i][0];
4722             int y1 = y + xy[i][1];
4723
4724             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4725             {
4726               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4727                 MovDir[x][y] = direction[0][i];
4728               else
4729                 MovDir[x][y] = direction[1][i];
4730
4731               break;
4732             }
4733           }
4734         }                
4735       }
4736       else
4737       {
4738         MovDir[x][y] = 1 << RND(4);
4739
4740         if (element != EL_BUG &&
4741             element != EL_SPACESHIP &&
4742             element != EL_BD_BUTTERFLY &&
4743             element != EL_BD_FIREFLY)
4744           break;
4745
4746         for (i = 0; i < NUM_DIRECTIONS; i++)
4747         {
4748           int x1 = x + xy[i][0];
4749           int y1 = y + xy[i][1];
4750
4751           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4752           {
4753             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4754             {
4755               MovDir[x][y] = direction[0][i];
4756               break;
4757             }
4758             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4759                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4760             {
4761               MovDir[x][y] = direction[1][i];
4762               break;
4763             }
4764           }
4765         }
4766       }
4767       break;
4768   }
4769
4770   GfxDir[x][y] = MovDir[x][y];
4771 }
4772
4773 void InitAmoebaNr(int x, int y)
4774 {
4775   int i;
4776   int group_nr = AmoebeNachbarNr(x, y);
4777
4778   if (group_nr == 0)
4779   {
4780     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4781     {
4782       if (AmoebaCnt[i] == 0)
4783       {
4784         group_nr = i;
4785         break;
4786       }
4787     }
4788   }
4789
4790   AmoebaNr[x][y] = group_nr;
4791   AmoebaCnt[group_nr]++;
4792   AmoebaCnt2[group_nr]++;
4793 }
4794
4795 static void PlayerWins(struct PlayerInfo *player)
4796 {
4797   player->LevelSolved = TRUE;
4798   player->GameOver = TRUE;
4799
4800   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4801                          level.native_em_level->lev->score : player->score);
4802
4803   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4804   player->LevelSolved_CountingScore = player->score_final;
4805 }
4806
4807 void GameWon()
4808 {
4809   static int time, time_final;
4810   static int score, score_final;
4811   static int game_over_delay_1 = 0;
4812   static int game_over_delay_2 = 0;
4813   int game_over_delay_value_1 = 50;
4814   int game_over_delay_value_2 = 50;
4815
4816   if (!local_player->LevelSolved_GameWon)
4817   {
4818     int i;
4819
4820     /* do not start end game actions before the player stops moving (to exit) */
4821     if (local_player->MovPos)
4822       return;
4823
4824     local_player->LevelSolved_GameWon = TRUE;
4825     local_player->LevelSolved_SaveTape = tape.recording;
4826     local_player->LevelSolved_SaveScore = !tape.playing;
4827
4828     if (tape.auto_play)         /* tape might already be stopped here */
4829       tape.auto_play_level_solved = TRUE;
4830
4831 #if 1
4832     TapeStop();
4833 #endif
4834
4835     game_over_delay_1 = game_over_delay_value_1;
4836     game_over_delay_2 = game_over_delay_value_2;
4837
4838     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4839     score = score_final = local_player->score_final;
4840
4841     if (TimeLeft > 0)
4842     {
4843       time_final = 0;
4844       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4845     }
4846     else if (level.time == 0 && TimePlayed < 999)
4847     {
4848       time_final = 999;
4849       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4850     }
4851
4852     local_player->score_final = score_final;
4853
4854     if (level_editor_test_game)
4855     {
4856       time = time_final;
4857       score = score_final;
4858
4859 #if 1
4860       local_player->LevelSolved_CountingTime = time;
4861       local_player->LevelSolved_CountingScore = score;
4862
4863       game_panel_controls[GAME_PANEL_TIME].value = time;
4864       game_panel_controls[GAME_PANEL_SCORE].value = score;
4865
4866       DisplayGameControlValues();
4867 #else
4868       DrawGameValue_Time(time);
4869       DrawGameValue_Score(score);
4870 #endif
4871     }
4872
4873     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4874     {
4875       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4876       {
4877         /* close exit door after last player */
4878         if ((AllPlayersGone &&
4879              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4880               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4881               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4882             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4883             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885           int element = Feld[ExitX][ExitY];
4886
4887 #if 0
4888           if (element == EL_EM_EXIT_OPEN ||
4889               element == EL_EM_STEEL_EXIT_OPEN)
4890           {
4891             Bang(ExitX, ExitY);
4892           }
4893           else
4894 #endif
4895           {
4896             Feld[ExitX][ExitY] =
4897               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4898                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4899                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4900                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4901                EL_EM_STEEL_EXIT_CLOSING);
4902
4903             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4904           }
4905         }
4906
4907         /* player disappears */
4908         DrawLevelField(ExitX, ExitY);
4909       }
4910
4911       for (i = 0; i < MAX_PLAYERS; i++)
4912       {
4913         struct PlayerInfo *player = &stored_player[i];
4914
4915         if (player->present)
4916         {
4917           RemovePlayer(player);
4918
4919           /* player disappears */
4920           DrawLevelField(player->jx, player->jy);
4921         }
4922       }
4923     }
4924
4925     PlaySound(SND_GAME_WINNING);
4926   }
4927
4928   if (game_over_delay_1 > 0)
4929   {
4930     game_over_delay_1--;
4931
4932     return;
4933   }
4934
4935   if (time != time_final)
4936   {
4937     int time_to_go = ABS(time_final - time);
4938     int time_count_dir = (time < time_final ? +1 : -1);
4939     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4940
4941     time  += time_count_steps * time_count_dir;
4942     score += time_count_steps * level.score[SC_TIME_BONUS];
4943
4944 #if 1
4945     local_player->LevelSolved_CountingTime = time;
4946     local_player->LevelSolved_CountingScore = score;
4947
4948     game_panel_controls[GAME_PANEL_TIME].value = time;
4949     game_panel_controls[GAME_PANEL_SCORE].value = score;
4950
4951     DisplayGameControlValues();
4952 #else
4953     DrawGameValue_Time(time);
4954     DrawGameValue_Score(score);
4955 #endif
4956
4957     if (time == time_final)
4958       StopSound(SND_GAME_LEVELTIME_BONUS);
4959     else if (setup.sound_loops)
4960       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4961     else
4962       PlaySound(SND_GAME_LEVELTIME_BONUS);
4963
4964     return;
4965   }
4966
4967   local_player->LevelSolved_PanelOff = TRUE;
4968
4969   if (game_over_delay_2 > 0)
4970   {
4971     game_over_delay_2--;
4972
4973     return;
4974   }
4975
4976 #if 1
4977   GameEnd();
4978 #endif
4979 }
4980
4981 void GameEnd()
4982 {
4983   int hi_pos;
4984   boolean raise_level = FALSE;
4985
4986   local_player->LevelSolved_GameEnd = TRUE;
4987
4988   CloseDoor(DOOR_CLOSE_1);
4989
4990   if (local_player->LevelSolved_SaveTape)
4991   {
4992 #if 0
4993     TapeStop();
4994 #endif
4995
4996 #if 1
4997     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4998 #else
4999     SaveTape(tape.level_nr);            /* ask to save tape */
5000 #endif
5001   }
5002
5003   if (level_editor_test_game)
5004   {
5005     game_status = GAME_MODE_MAIN;
5006
5007 #if 1
5008     DrawAndFadeInMainMenu(REDRAW_FIELD);
5009 #else
5010     DrawMainMenu();
5011 #endif
5012
5013     return;
5014   }
5015
5016   if (!local_player->LevelSolved_SaveScore)
5017   {
5018 #if 1
5019     FadeOut(REDRAW_FIELD);
5020 #endif
5021
5022     game_status = GAME_MODE_MAIN;
5023
5024     DrawAndFadeInMainMenu(REDRAW_FIELD);
5025
5026     return;
5027   }
5028
5029   if (level_nr == leveldir_current->handicap_level)
5030   {
5031     leveldir_current->handicap_level++;
5032     SaveLevelSetup_SeriesInfo();
5033   }
5034
5035   if (level_nr < leveldir_current->last_level)
5036     raise_level = TRUE;                 /* advance to next level */
5037
5038   if ((hi_pos = NewHiScore()) >= 0) 
5039   {
5040     game_status = GAME_MODE_SCORES;
5041
5042     DrawHallOfFame(hi_pos);
5043
5044     if (raise_level)
5045     {
5046       level_nr++;
5047       TapeErase();
5048     }
5049   }
5050   else
5051   {
5052 #if 1
5053     FadeOut(REDRAW_FIELD);
5054 #endif
5055
5056     game_status = GAME_MODE_MAIN;
5057
5058     if (raise_level)
5059     {
5060       level_nr++;
5061       TapeErase();
5062     }
5063
5064     DrawAndFadeInMainMenu(REDRAW_FIELD);
5065   }
5066 }
5067
5068 int NewHiScore()
5069 {
5070   int k, l;
5071   int position = -1;
5072
5073   LoadScore(level_nr);
5074
5075   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5076       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5077     return -1;
5078
5079   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5080   {
5081     if (local_player->score_final > highscore[k].Score)
5082     {
5083       /* player has made it to the hall of fame */
5084
5085       if (k < MAX_SCORE_ENTRIES - 1)
5086       {
5087         int m = MAX_SCORE_ENTRIES - 1;
5088
5089 #ifdef ONE_PER_NAME
5090         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5091           if (strEqual(setup.player_name, highscore[l].Name))
5092             m = l;
5093         if (m == k)     /* player's new highscore overwrites his old one */
5094           goto put_into_list;
5095 #endif
5096
5097         for (l = m; l > k; l--)
5098         {
5099           strcpy(highscore[l].Name, highscore[l - 1].Name);
5100           highscore[l].Score = highscore[l - 1].Score;
5101         }
5102       }
5103
5104 #ifdef ONE_PER_NAME
5105       put_into_list:
5106 #endif
5107       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5108       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5109       highscore[k].Score = local_player->score_final; 
5110       position = k;
5111       break;
5112     }
5113
5114 #ifdef ONE_PER_NAME
5115     else if (!strncmp(setup.player_name, highscore[k].Name,
5116                       MAX_PLAYER_NAME_LEN))
5117       break;    /* player already there with a higher score */
5118 #endif
5119
5120   }
5121
5122   if (position >= 0) 
5123     SaveScore(level_nr);
5124
5125   return position;
5126 }
5127
5128 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5129 {
5130   int element = Feld[x][y];
5131   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5132   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5133   int horiz_move = (dx != 0);
5134   int sign = (horiz_move ? dx : dy);
5135   int step = sign * element_info[element].move_stepsize;
5136
5137   /* special values for move stepsize for spring and things on conveyor belt */
5138   if (horiz_move)
5139   {
5140     if (CAN_FALL(element) &&
5141         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5142       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5143     else if (element == EL_SPRING)
5144       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5145   }
5146
5147   return step;
5148 }
5149
5150 inline static int getElementMoveStepsize(int x, int y)
5151 {
5152   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5153 }
5154
5155 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5156 {
5157   if (player->GfxAction != action || player->GfxDir != dir)
5158   {
5159 #if 0
5160     printf("Player frame reset! (%d => %d, %d => %d)\n",
5161            player->GfxAction, action, player->GfxDir, dir);
5162 #endif
5163
5164     player->GfxAction = action;
5165     player->GfxDir = dir;
5166     player->Frame = 0;
5167     player->StepFrame = 0;
5168   }
5169 }
5170
5171 #if USE_GFX_RESET_GFX_ANIMATION
5172 static void ResetGfxFrame(int x, int y, boolean redraw)
5173 {
5174   int element = Feld[x][y];
5175   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5176   int last_gfx_frame = GfxFrame[x][y];
5177
5178   if (graphic_info[graphic].anim_global_sync)
5179     GfxFrame[x][y] = FrameCounter;
5180   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5181     GfxFrame[x][y] = CustomValue[x][y];
5182   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5183     GfxFrame[x][y] = element_info[element].collect_score;
5184   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5185     GfxFrame[x][y] = ChangeDelay[x][y];
5186
5187   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5188     DrawLevelGraphicAnimation(x, y, graphic);
5189 }
5190 #endif
5191
5192 static void ResetGfxAnimation(int x, int y)
5193 {
5194   GfxAction[x][y] = ACTION_DEFAULT;
5195   GfxDir[x][y] = MovDir[x][y];
5196   GfxFrame[x][y] = 0;
5197
5198 #if USE_GFX_RESET_GFX_ANIMATION
5199   ResetGfxFrame(x, y, FALSE);
5200 #endif
5201 }
5202
5203 static void ResetRandomAnimationValue(int x, int y)
5204 {
5205   GfxRandom[x][y] = INIT_GFX_RANDOM();
5206 }
5207
5208 void InitMovingField(int x, int y, int direction)
5209 {
5210   int element = Feld[x][y];
5211   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5212   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5213   int newx = x + dx;
5214   int newy = y + dy;
5215   boolean is_moving_before, is_moving_after;
5216 #if 0
5217   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5218 #endif
5219
5220   /* check if element was/is moving or being moved before/after mode change */
5221 #if 1
5222 #if 1
5223   is_moving_before = (WasJustMoving[x][y] != 0);
5224 #else
5225   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5226   is_moving_before = WasJustMoving[x][y];
5227 #endif
5228 #else
5229   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5230 #endif
5231   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5232
5233   /* reset animation only for moving elements which change direction of moving
5234      or which just started or stopped moving
5235      (else CEs with property "can move" / "not moving" are reset each frame) */
5236 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5237 #if 1
5238   if (is_moving_before != is_moving_after ||
5239       direction != MovDir[x][y])
5240     ResetGfxAnimation(x, y);
5241 #else
5242   if ((is_moving_before || is_moving_after) && !continues_moving)
5243     ResetGfxAnimation(x, y);
5244 #endif
5245 #else
5246   if (!continues_moving)
5247     ResetGfxAnimation(x, y);
5248 #endif
5249
5250   MovDir[x][y] = direction;
5251   GfxDir[x][y] = direction;
5252
5253 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5254   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5255                      direction == MV_DOWN && CAN_FALL(element) ?
5256                      ACTION_FALLING : ACTION_MOVING);
5257 #else
5258   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5259                      ACTION_FALLING : ACTION_MOVING);
5260 #endif
5261
5262   /* this is needed for CEs with property "can move" / "not moving" */
5263
5264   if (is_moving_after)
5265   {
5266     if (Feld[newx][newy] == EL_EMPTY)
5267       Feld[newx][newy] = EL_BLOCKED;
5268
5269     MovDir[newx][newy] = MovDir[x][y];
5270
5271 #if USE_NEW_CUSTOM_VALUE
5272     CustomValue[newx][newy] = CustomValue[x][y];
5273 #endif
5274
5275     GfxFrame[newx][newy] = GfxFrame[x][y];
5276     GfxRandom[newx][newy] = GfxRandom[x][y];
5277     GfxAction[newx][newy] = GfxAction[x][y];
5278     GfxDir[newx][newy] = GfxDir[x][y];
5279   }
5280 }
5281
5282 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5283 {
5284   int direction = MovDir[x][y];
5285   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5286   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5287
5288   *goes_to_x = newx;
5289   *goes_to_y = newy;
5290 }
5291
5292 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5293 {
5294   int oldx = x, oldy = y;
5295   int direction = MovDir[x][y];
5296
5297   if (direction == MV_LEFT)
5298     oldx++;
5299   else if (direction == MV_RIGHT)
5300     oldx--;
5301   else if (direction == MV_UP)
5302     oldy++;
5303   else if (direction == MV_DOWN)
5304     oldy--;
5305
5306   *comes_from_x = oldx;
5307   *comes_from_y = oldy;
5308 }
5309
5310 int MovingOrBlocked2Element(int x, int y)
5311 {
5312   int element = Feld[x][y];
5313
5314   if (element == EL_BLOCKED)
5315   {
5316     int oldx, oldy;
5317
5318     Blocked2Moving(x, y, &oldx, &oldy);
5319     return Feld[oldx][oldy];
5320   }
5321   else
5322     return element;
5323 }
5324
5325 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5326 {
5327   /* like MovingOrBlocked2Element(), but if element is moving
5328      and (x,y) is the field the moving element is just leaving,
5329      return EL_BLOCKED instead of the element value */
5330   int element = Feld[x][y];
5331
5332   if (IS_MOVING(x, y))
5333   {
5334     if (element == EL_BLOCKED)
5335     {
5336       int oldx, oldy;
5337
5338       Blocked2Moving(x, y, &oldx, &oldy);
5339       return Feld[oldx][oldy];
5340     }
5341     else
5342       return EL_BLOCKED;
5343   }
5344   else
5345     return element;
5346 }
5347
5348 static void RemoveField(int x, int y)
5349 {
5350   Feld[x][y] = EL_EMPTY;
5351
5352   MovPos[x][y] = 0;
5353   MovDir[x][y] = 0;
5354   MovDelay[x][y] = 0;
5355
5356 #if USE_NEW_CUSTOM_VALUE
5357   CustomValue[x][y] = 0;
5358 #endif
5359
5360   AmoebaNr[x][y] = 0;
5361   ChangeDelay[x][y] = 0;
5362   ChangePage[x][y] = -1;
5363   Pushed[x][y] = FALSE;
5364
5365 #if 0
5366   ExplodeField[x][y] = EX_TYPE_NONE;
5367 #endif
5368
5369   GfxElement[x][y] = EL_UNDEFINED;
5370   GfxAction[x][y] = ACTION_DEFAULT;
5371   GfxDir[x][y] = MV_NONE;
5372 #if 0
5373   /* !!! this would prevent the removed tile from being redrawn !!! */
5374   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5375 #endif
5376 }
5377
5378 void RemoveMovingField(int x, int y)
5379 {
5380   int oldx = x, oldy = y, newx = x, newy = y;
5381   int element = Feld[x][y];
5382   int next_element = EL_UNDEFINED;
5383
5384   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5385     return;
5386
5387   if (IS_MOVING(x, y))
5388   {
5389     Moving2Blocked(x, y, &newx, &newy);
5390
5391     if (Feld[newx][newy] != EL_BLOCKED)
5392     {
5393       /* element is moving, but target field is not free (blocked), but
5394          already occupied by something different (example: acid pool);
5395          in this case, only remove the moving field, but not the target */
5396
5397       RemoveField(oldx, oldy);
5398
5399       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401       TEST_DrawLevelField(oldx, oldy);
5402
5403       return;
5404     }
5405   }
5406   else if (element == EL_BLOCKED)
5407   {
5408     Blocked2Moving(x, y, &oldx, &oldy);
5409     if (!IS_MOVING(oldx, oldy))
5410       return;
5411   }
5412
5413   if (element == EL_BLOCKED &&
5414       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5415        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5416        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5417        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5418        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5419        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5420     next_element = get_next_element(Feld[oldx][oldy]);
5421
5422   RemoveField(oldx, oldy);
5423   RemoveField(newx, newy);
5424
5425   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5426
5427   if (next_element != EL_UNDEFINED)
5428     Feld[oldx][oldy] = next_element;
5429
5430   TEST_DrawLevelField(oldx, oldy);
5431   TEST_DrawLevelField(newx, newy);
5432 }
5433
5434 void DrawDynamite(int x, int y)
5435 {
5436   int sx = SCREENX(x), sy = SCREENY(y);
5437   int graphic = el2img(Feld[x][y]);
5438   int frame;
5439
5440   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5441     return;
5442
5443   if (IS_WALKABLE_INSIDE(Back[x][y]))
5444     return;
5445
5446   if (Back[x][y])
5447     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5448   else if (Store[x][y])
5449     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5450
5451   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5452
5453   if (Back[x][y] || Store[x][y])
5454     DrawGraphicThruMask(sx, sy, graphic, frame);
5455   else
5456     DrawGraphic(sx, sy, graphic, frame);
5457 }
5458
5459 void CheckDynamite(int x, int y)
5460 {
5461   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5462   {
5463     MovDelay[x][y]--;
5464
5465     if (MovDelay[x][y] != 0)
5466     {
5467       DrawDynamite(x, y);
5468       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5469
5470       return;
5471     }
5472   }
5473
5474   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5475
5476   Bang(x, y);
5477 }
5478
5479 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5480 {
5481   boolean num_checked_players = 0;
5482   int i;
5483
5484   for (i = 0; i < MAX_PLAYERS; i++)
5485   {
5486     if (stored_player[i].active)
5487     {
5488       int sx = stored_player[i].jx;
5489       int sy = stored_player[i].jy;
5490
5491       if (num_checked_players == 0)
5492       {
5493         *sx1 = *sx2 = sx;
5494         *sy1 = *sy2 = sy;
5495       }
5496       else
5497       {
5498         *sx1 = MIN(*sx1, sx);
5499         *sy1 = MIN(*sy1, sy);
5500         *sx2 = MAX(*sx2, sx);
5501         *sy2 = MAX(*sy2, sy);
5502       }
5503
5504       num_checked_players++;
5505     }
5506   }
5507 }
5508
5509 static boolean checkIfAllPlayersFitToScreen_RND()
5510 {
5511   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5512
5513   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5514
5515   return (sx2 - sx1 < SCR_FIELDX &&
5516           sy2 - sy1 < SCR_FIELDY);
5517 }
5518
5519 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5520 {
5521   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5522
5523   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5524
5525   *sx = (sx1 + sx2) / 2;
5526   *sy = (sy1 + sy2) / 2;
5527 }
5528
5529 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5530                         boolean center_screen, boolean quick_relocation)
5531 {
5532   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5533   boolean no_delay = (tape.warp_forward);
5534   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5535   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5536
5537   if (quick_relocation)
5538   {
5539     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5540     {
5541       if (!level.shifted_relocation || center_screen)
5542       {
5543         /* quick relocation (without scrolling), with centering of screen */
5544
5545         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5546                     x > SBX_Right + MIDPOSX ? SBX_Right :
5547                     x - MIDPOSX);
5548
5549         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5550                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5551                     y - MIDPOSY);
5552       }
5553       else
5554       {
5555         /* quick relocation (without scrolling), but do not center screen */
5556
5557         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5558                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5559                                old_x - MIDPOSX);
5560
5561         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5562                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5563                                old_y - MIDPOSY);
5564
5565         int offset_x = x + (scroll_x - center_scroll_x);
5566         int offset_y = y + (scroll_y - center_scroll_y);
5567
5568         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5569                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5570                     offset_x - MIDPOSX);
5571
5572         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5573                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5574                     offset_y - MIDPOSY);
5575       }
5576     }
5577     else
5578     {
5579 #if 1
5580       if (!level.shifted_relocation || center_screen)
5581       {
5582         /* quick relocation (without scrolling), with centering of screen */
5583
5584         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5585                     x > SBX_Right + MIDPOSX ? SBX_Right :
5586                     x - MIDPOSX);
5587
5588         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5589                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5590                     y - MIDPOSY);
5591       }
5592       else
5593       {
5594         /* quick relocation (without scrolling), but do not center screen */
5595
5596         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5597                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5598                                old_x - MIDPOSX);
5599
5600         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5601                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5602                                old_y - MIDPOSY);
5603
5604         int offset_x = x + (scroll_x - center_scroll_x);
5605         int offset_y = y + (scroll_y - center_scroll_y);
5606
5607         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5608                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5609                     offset_x - MIDPOSX);
5610
5611         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5612                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5613                     offset_y - MIDPOSY);
5614       }
5615 #else
5616       /* quick relocation (without scrolling), inside visible screen area */
5617
5618       int offset = game.scroll_delay_value;
5619
5620       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5621           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5622         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5623
5624       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5625           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5626         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5627
5628       /* don't scroll over playfield boundaries */
5629       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5630         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5631
5632       /* don't scroll over playfield boundaries */
5633       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5634         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5635 #endif
5636     }
5637
5638     RedrawPlayfield(TRUE, 0,0,0,0);
5639   }
5640   else
5641   {
5642 #if 1
5643     int scroll_xx, scroll_yy;
5644
5645     if (!level.shifted_relocation || center_screen)
5646     {
5647       /* visible relocation (with scrolling), with centering of screen */
5648
5649       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5650                    x > SBX_Right + MIDPOSX ? SBX_Right :
5651                    x - MIDPOSX);
5652
5653       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655                    y - MIDPOSY);
5656     }
5657     else
5658     {
5659       /* visible relocation (with scrolling), but do not center screen */
5660
5661       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5662                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5663                              old_x - MIDPOSX);
5664
5665       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5666                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5667                              old_y - MIDPOSY);
5668
5669       int offset_x = x + (scroll_x - center_scroll_x);
5670       int offset_y = y + (scroll_y - center_scroll_y);
5671
5672       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5673                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5674                    offset_x - MIDPOSX);
5675
5676       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5677                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5678                    offset_y - MIDPOSY);
5679     }
5680
5681 #else
5682
5683     /* visible relocation (with scrolling), with centering of screen */
5684
5685     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5686                      x > SBX_Right + MIDPOSX ? SBX_Right :
5687                      x - MIDPOSX);
5688
5689     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5690                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5691                      y - MIDPOSY);
5692 #endif
5693
5694     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5695
5696     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5697     {
5698       int dx = 0, dy = 0;
5699       int fx = FX, fy = FY;
5700
5701       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5702       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5703
5704       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5705         break;
5706
5707       scroll_x -= dx;
5708       scroll_y -= dy;
5709
5710       fx += dx * TILEX / 2;
5711       fy += dy * TILEY / 2;
5712
5713       ScrollLevel(dx, dy);
5714       DrawAllPlayers();
5715
5716       /* scroll in two steps of half tile size to make things smoother */
5717       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5718       FlushDisplay();
5719       Delay(wait_delay_value);
5720
5721       /* scroll second step to align at full tile size */
5722       BackToFront();
5723       Delay(wait_delay_value);
5724     }
5725
5726     DrawAllPlayers();
5727     BackToFront();
5728     Delay(wait_delay_value);
5729   }
5730 }
5731
5732 void RelocatePlayer(int jx, int jy, int el_player_raw)
5733 {
5734   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5735   int player_nr = GET_PLAYER_NR(el_player);
5736   struct PlayerInfo *player = &stored_player[player_nr];
5737   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5738   boolean no_delay = (tape.warp_forward);
5739   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5740   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5741   int old_jx = player->jx;
5742   int old_jy = player->jy;
5743   int old_element = Feld[old_jx][old_jy];
5744   int element = Feld[jx][jy];
5745   boolean player_relocated = (old_jx != jx || old_jy != jy);
5746
5747   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5748   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5749   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5750   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5751   int leave_side_horiz = move_dir_horiz;
5752   int leave_side_vert  = move_dir_vert;
5753   int enter_side = enter_side_horiz | enter_side_vert;
5754   int leave_side = leave_side_horiz | leave_side_vert;
5755
5756   if (player->GameOver)         /* do not reanimate dead player */
5757     return;
5758
5759   if (!player_relocated)        /* no need to relocate the player */
5760     return;
5761
5762   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5763   {
5764     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5765     DrawLevelField(jx, jy);
5766   }
5767
5768   if (player->present)
5769   {
5770     while (player->MovPos)
5771     {
5772       ScrollPlayer(player, SCROLL_GO_ON);
5773       ScrollScreen(NULL, SCROLL_GO_ON);
5774
5775       AdvanceFrameAndPlayerCounters(player->index_nr);
5776
5777       DrawPlayer(player);
5778
5779       BackToFront();
5780       Delay(wait_delay_value);
5781     }
5782
5783     DrawPlayer(player);         /* needed here only to cleanup last field */
5784     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5785
5786     player->is_moving = FALSE;
5787   }
5788
5789   if (IS_CUSTOM_ELEMENT(old_element))
5790     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5791                                CE_LEFT_BY_PLAYER,
5792                                player->index_bit, leave_side);
5793
5794   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5795                                       CE_PLAYER_LEAVES_X,
5796                                       player->index_bit, leave_side);
5797
5798   Feld[jx][jy] = el_player;
5799   InitPlayerField(jx, jy, el_player, TRUE);
5800
5801   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5802      possible that the relocation target field did not contain a player element,
5803      but a walkable element, to which the new player was relocated -- in this
5804      case, restore that (already initialized!) element on the player field */
5805   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5806   {
5807     Feld[jx][jy] = element;     /* restore previously existing element */
5808 #if 0
5809     /* !!! do not initialize already initialized element a second time !!! */
5810     /* (this causes at least problems with "element creation" CE trigger for
5811        already existing elements, and existing Sokoban fields counted twice) */
5812     InitField(jx, jy, FALSE);
5813 #endif
5814   }
5815
5816   /* only visually relocate centered player */
5817   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5818                      FALSE, level.instant_relocation);
5819
5820   TestIfPlayerTouchesBadThing(jx, jy);
5821   TestIfPlayerTouchesCustomElement(jx, jy);
5822
5823   if (IS_CUSTOM_ELEMENT(element))
5824     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5825                                player->index_bit, enter_side);
5826
5827   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5828                                       player->index_bit, enter_side);
5829
5830 #if 1
5831   if (player->is_switching)
5832   {
5833     /* ensure that relocation while still switching an element does not cause
5834        a new element to be treated as also switched directly after relocation
5835        (this is important for teleporter switches that teleport the player to
5836        a place where another teleporter switch is in the same direction, which
5837        would then incorrectly be treated as immediately switched before the
5838        direction key that caused the switch was released) */
5839
5840     player->switch_x += jx - old_jx;
5841     player->switch_y += jy - old_jy;
5842   }
5843 #endif
5844 }
5845
5846 void Explode(int ex, int ey, int phase, int mode)
5847 {
5848   int x, y;
5849   int last_phase;
5850   int border_element;
5851
5852   /* !!! eliminate this variable !!! */
5853   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5854
5855   if (game.explosions_delayed)
5856   {
5857     ExplodeField[ex][ey] = mode;
5858     return;
5859   }
5860
5861   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5862   {
5863     int center_element = Feld[ex][ey];
5864     int artwork_element, explosion_element;     /* set these values later */
5865
5866 #if 0
5867     /* --- This is only really needed (and now handled) in "Impact()". --- */
5868     /* do not explode moving elements that left the explode field in time */
5869     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5870         center_element == EL_EMPTY &&
5871         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5872       return;
5873 #endif
5874
5875 #if 0
5876     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5877     if (mode == EX_TYPE_NORMAL ||
5878         mode == EX_TYPE_CENTER ||
5879         mode == EX_TYPE_CROSS)
5880       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5881 #endif
5882
5883     /* remove things displayed in background while burning dynamite */
5884     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5885       Back[ex][ey] = 0;
5886
5887     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5888     {
5889       /* put moving element to center field (and let it explode there) */
5890       center_element = MovingOrBlocked2Element(ex, ey);
5891       RemoveMovingField(ex, ey);
5892       Feld[ex][ey] = center_element;
5893     }
5894
5895     /* now "center_element" is finally determined -- set related values now */
5896     artwork_element = center_element;           /* for custom player artwork */
5897     explosion_element = center_element;         /* for custom player artwork */
5898
5899     if (IS_PLAYER(ex, ey))
5900     {
5901       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5902
5903       artwork_element = stored_player[player_nr].artwork_element;
5904
5905       if (level.use_explosion_element[player_nr])
5906       {
5907         explosion_element = level.explosion_element[player_nr];
5908         artwork_element = explosion_element;
5909       }
5910     }
5911
5912 #if 1
5913     if (mode == EX_TYPE_NORMAL ||
5914         mode == EX_TYPE_CENTER ||
5915         mode == EX_TYPE_CROSS)
5916       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5917 #endif
5918
5919     last_phase = element_info[explosion_element].explosion_delay + 1;
5920
5921     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5922     {
5923       int xx = x - ex + 1;
5924       int yy = y - ey + 1;
5925       int element;
5926
5927       if (!IN_LEV_FIELD(x, y) ||
5928           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5929           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5930         continue;
5931
5932       element = Feld[x][y];
5933
5934       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5935       {
5936         element = MovingOrBlocked2Element(x, y);
5937
5938         if (!IS_EXPLOSION_PROOF(element))
5939           RemoveMovingField(x, y);
5940       }
5941
5942       /* indestructible elements can only explode in center (but not flames) */
5943       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5944                                            mode == EX_TYPE_BORDER)) ||
5945           element == EL_FLAMES)
5946         continue;
5947
5948       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5949          behaviour, for example when touching a yamyam that explodes to rocks
5950          with active deadly shield, a rock is created under the player !!! */
5951       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5952 #if 0
5953       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5954           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5955            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5956 #else
5957       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5958 #endif
5959       {
5960         if (IS_ACTIVE_BOMB(element))
5961         {
5962           /* re-activate things under the bomb like gate or penguin */
5963           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5964           Back[x][y] = 0;
5965         }
5966
5967         continue;
5968       }
5969
5970       /* save walkable background elements while explosion on same tile */
5971       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5972           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5973         Back[x][y] = element;
5974
5975       /* ignite explodable elements reached by other explosion */
5976       if (element == EL_EXPLOSION)
5977         element = Store2[x][y];
5978
5979       if (AmoebaNr[x][y] &&
5980           (element == EL_AMOEBA_FULL ||
5981            element == EL_BD_AMOEBA ||
5982            element == EL_AMOEBA_GROWING))
5983       {
5984         AmoebaCnt[AmoebaNr[x][y]]--;
5985         AmoebaCnt2[AmoebaNr[x][y]]--;
5986       }
5987
5988       RemoveField(x, y);
5989
5990       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5991       {
5992         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5993
5994         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5995
5996         if (PLAYERINFO(ex, ey)->use_murphy)
5997           Store[x][y] = EL_EMPTY;
5998       }
5999
6000       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6001          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6002       else if (ELEM_IS_PLAYER(center_element))
6003         Store[x][y] = EL_EMPTY;
6004       else if (center_element == EL_YAMYAM)
6005         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6006       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6007         Store[x][y] = element_info[center_element].content.e[xx][yy];
6008 #if 1
6009       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6010          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6011          otherwise) -- FIX THIS !!! */
6012       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6013         Store[x][y] = element_info[element].content.e[1][1];
6014 #else
6015       else if (!CAN_EXPLODE(element))
6016         Store[x][y] = element_info[element].content.e[1][1];
6017 #endif
6018       else
6019         Store[x][y] = EL_EMPTY;
6020
6021       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6022           center_element == EL_AMOEBA_TO_DIAMOND)
6023         Store2[x][y] = element;
6024
6025       Feld[x][y] = EL_EXPLOSION;
6026       GfxElement[x][y] = artwork_element;
6027
6028       ExplodePhase[x][y] = 1;
6029       ExplodeDelay[x][y] = last_phase;
6030
6031       Stop[x][y] = TRUE;
6032     }
6033
6034     if (center_element == EL_YAMYAM)
6035       game.yamyam_content_nr =
6036         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6037
6038     return;
6039   }
6040
6041   if (Stop[ex][ey])
6042     return;
6043
6044   x = ex;
6045   y = ey;
6046
6047   if (phase == 1)
6048     GfxFrame[x][y] = 0;         /* restart explosion animation */
6049
6050   last_phase = ExplodeDelay[x][y];
6051
6052   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6053
6054 #ifdef DEBUG
6055
6056   /* activate this even in non-DEBUG version until cause for crash in
6057      getGraphicAnimationFrame() (see below) is found and eliminated */
6058
6059 #endif
6060 #if 1
6061
6062 #if 1
6063   /* this can happen if the player leaves an explosion just in time */
6064   if (GfxElement[x][y] == EL_UNDEFINED)
6065     GfxElement[x][y] = EL_EMPTY;
6066 #else
6067   if (GfxElement[x][y] == EL_UNDEFINED)
6068   {
6069     printf("\n\n");
6070     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6071     printf("Explode(): This should never happen!\n");
6072     printf("\n\n");
6073
6074     GfxElement[x][y] = EL_EMPTY;
6075   }
6076 #endif
6077
6078 #endif
6079
6080   border_element = Store2[x][y];
6081   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6082     border_element = StorePlayer[x][y];
6083
6084   if (phase == element_info[border_element].ignition_delay ||
6085       phase == last_phase)
6086   {
6087     boolean border_explosion = FALSE;
6088
6089     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6090         !PLAYER_EXPLOSION_PROTECTED(x, y))
6091     {
6092       KillPlayerUnlessExplosionProtected(x, y);
6093       border_explosion = TRUE;
6094     }
6095     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6096     {
6097       Feld[x][y] = Store2[x][y];
6098       Store2[x][y] = 0;
6099       Bang(x, y);
6100       border_explosion = TRUE;
6101     }
6102     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6103     {
6104       AmoebeUmwandeln(x, y);
6105       Store2[x][y] = 0;
6106       border_explosion = TRUE;
6107     }
6108
6109     /* if an element just explodes due to another explosion (chain-reaction),
6110        do not immediately end the new explosion when it was the last frame of
6111        the explosion (as it would be done in the following "if"-statement!) */
6112     if (border_explosion && phase == last_phase)
6113       return;
6114   }
6115
6116   if (phase == last_phase)
6117   {
6118     int element;
6119
6120     element = Feld[x][y] = Store[x][y];
6121     Store[x][y] = Store2[x][y] = 0;
6122     GfxElement[x][y] = EL_UNDEFINED;
6123
6124     /* player can escape from explosions and might therefore be still alive */
6125     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6126         element <= EL_PLAYER_IS_EXPLODING_4)
6127     {
6128       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6129       int explosion_element = EL_PLAYER_1 + player_nr;
6130       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6131       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6132
6133       if (level.use_explosion_element[player_nr])
6134         explosion_element = level.explosion_element[player_nr];
6135
6136       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6137                     element_info[explosion_element].content.e[xx][yy]);
6138     }
6139
6140     /* restore probably existing indestructible background element */
6141     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6142       element = Feld[x][y] = Back[x][y];
6143     Back[x][y] = 0;
6144
6145     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6146     GfxDir[x][y] = MV_NONE;
6147     ChangeDelay[x][y] = 0;
6148     ChangePage[x][y] = -1;
6149
6150 #if USE_NEW_CUSTOM_VALUE
6151     CustomValue[x][y] = 0;
6152 #endif
6153
6154     InitField_WithBug2(x, y, FALSE);
6155
6156     TEST_DrawLevelField(x, y);
6157
6158     TestIfElementTouchesCustomElement(x, y);
6159
6160     if (GFX_CRUMBLED(element))
6161       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6162
6163     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6164       StorePlayer[x][y] = 0;
6165
6166     if (ELEM_IS_PLAYER(element))
6167       RelocatePlayer(x, y, element);
6168   }
6169   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6170   {
6171     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6172     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6173
6174     if (phase == delay)
6175       TEST_DrawLevelFieldCrumbled(x, y);
6176
6177     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6178     {
6179       DrawLevelElement(x, y, Back[x][y]);
6180       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6181     }
6182     else if (IS_WALKABLE_UNDER(Back[x][y]))
6183     {
6184       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6185       DrawLevelElementThruMask(x, y, Back[x][y]);
6186     }
6187     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6188       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6189   }
6190 }
6191
6192 void DynaExplode(int ex, int ey)
6193 {
6194   int i, j;
6195   int dynabomb_element = Feld[ex][ey];
6196   int dynabomb_size = 1;
6197   boolean dynabomb_xl = FALSE;
6198   struct PlayerInfo *player;
6199   static int xy[4][2] =
6200   {
6201     { 0, -1 },
6202     { -1, 0 },
6203     { +1, 0 },
6204     { 0, +1 }
6205   };
6206
6207   if (IS_ACTIVE_BOMB(dynabomb_element))
6208   {
6209     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6210     dynabomb_size = player->dynabomb_size;
6211     dynabomb_xl = player->dynabomb_xl;
6212     player->dynabombs_left++;
6213   }
6214
6215   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6216
6217   for (i = 0; i < NUM_DIRECTIONS; i++)
6218   {
6219     for (j = 1; j <= dynabomb_size; j++)
6220     {
6221       int x = ex + j * xy[i][0];
6222       int y = ey + j * xy[i][1];
6223       int element;
6224
6225       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6226         break;
6227
6228       element = Feld[x][y];
6229
6230       /* do not restart explosions of fields with active bombs */
6231       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6232         continue;
6233
6234       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6235
6236       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6237           !IS_DIGGABLE(element) && !dynabomb_xl)
6238         break;
6239     }
6240   }
6241 }
6242
6243 void Bang(int x, int y)
6244 {
6245   int element = MovingOrBlocked2Element(x, y);
6246   int explosion_type = EX_TYPE_NORMAL;
6247
6248   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6249   {
6250     struct PlayerInfo *player = PLAYERINFO(x, y);
6251
6252 #if USE_FIX_CE_ACTION_WITH_PLAYER
6253     element = Feld[x][y] = player->initial_element;
6254 #else
6255     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6256                             player->element_nr);
6257 #endif
6258
6259     if (level.use_explosion_element[player->index_nr])
6260     {
6261       int explosion_element = level.explosion_element[player->index_nr];
6262
6263       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6264         explosion_type = EX_TYPE_CROSS;
6265       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6266         explosion_type = EX_TYPE_CENTER;
6267     }
6268   }
6269
6270   switch (element)
6271   {
6272     case EL_BUG:
6273     case EL_SPACESHIP:
6274     case EL_BD_BUTTERFLY:
6275     case EL_BD_FIREFLY:
6276     case EL_YAMYAM:
6277     case EL_DARK_YAMYAM:
6278     case EL_ROBOT:
6279     case EL_PACMAN:
6280     case EL_MOLE:
6281       RaiseScoreElement(element);
6282       break;
6283
6284     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6285     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6286     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6287     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6288     case EL_DYNABOMB_INCREASE_NUMBER:
6289     case EL_DYNABOMB_INCREASE_SIZE:
6290     case EL_DYNABOMB_INCREASE_POWER:
6291       explosion_type = EX_TYPE_DYNA;
6292       break;
6293
6294     case EL_DC_LANDMINE:
6295 #if 0
6296     case EL_EM_EXIT_OPEN:
6297     case EL_EM_STEEL_EXIT_OPEN:
6298 #endif
6299       explosion_type = EX_TYPE_CENTER;
6300       break;
6301
6302     case EL_PENGUIN:
6303     case EL_LAMP:
6304     case EL_LAMP_ACTIVE:
6305     case EL_AMOEBA_TO_DIAMOND:
6306       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6307         explosion_type = EX_TYPE_CENTER;
6308       break;
6309
6310     default:
6311       if (element_info[element].explosion_type == EXPLODES_CROSS)
6312         explosion_type = EX_TYPE_CROSS;
6313       else if (element_info[element].explosion_type == EXPLODES_1X1)
6314         explosion_type = EX_TYPE_CENTER;
6315       break;
6316   }
6317
6318   if (explosion_type == EX_TYPE_DYNA)
6319     DynaExplode(x, y);
6320   else
6321     Explode(x, y, EX_PHASE_START, explosion_type);
6322
6323   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6324 }
6325
6326 void SplashAcid(int x, int y)
6327 {
6328   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6329       (!IN_LEV_FIELD(x - 1, y - 2) ||
6330        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6331     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6332
6333   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6334       (!IN_LEV_FIELD(x + 1, y - 2) ||
6335        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6336     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6337
6338   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6339 }
6340
6341 static void InitBeltMovement()
6342 {
6343   static int belt_base_element[4] =
6344   {
6345     EL_CONVEYOR_BELT_1_LEFT,
6346     EL_CONVEYOR_BELT_2_LEFT,
6347     EL_CONVEYOR_BELT_3_LEFT,
6348     EL_CONVEYOR_BELT_4_LEFT
6349   };
6350   static int belt_base_active_element[4] =
6351   {
6352     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6354     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6355     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6356   };
6357
6358   int x, y, i, j;
6359
6360   /* set frame order for belt animation graphic according to belt direction */
6361   for (i = 0; i < NUM_BELTS; i++)
6362   {
6363     int belt_nr = i;
6364
6365     for (j = 0; j < NUM_BELT_PARTS; j++)
6366     {
6367       int element = belt_base_active_element[belt_nr] + j;
6368       int graphic_1 = el2img(element);
6369       int graphic_2 = el2panelimg(element);
6370
6371       if (game.belt_dir[i] == MV_LEFT)
6372       {
6373         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6374         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6375       }
6376       else
6377       {
6378         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6379         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6380       }
6381     }
6382   }
6383
6384   SCAN_PLAYFIELD(x, y)
6385   {
6386     int element = Feld[x][y];
6387
6388     for (i = 0; i < NUM_BELTS; i++)
6389     {
6390       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6391       {
6392         int e_belt_nr = getBeltNrFromBeltElement(element);
6393         int belt_nr = i;
6394
6395         if (e_belt_nr == belt_nr)
6396         {
6397           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6398
6399           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6400         }
6401       }
6402     }
6403   }
6404 }
6405
6406 static void ToggleBeltSwitch(int x, int y)
6407 {
6408   static int belt_base_element[4] =
6409   {
6410     EL_CONVEYOR_BELT_1_LEFT,
6411     EL_CONVEYOR_BELT_2_LEFT,
6412     EL_CONVEYOR_BELT_3_LEFT,
6413     EL_CONVEYOR_BELT_4_LEFT
6414   };
6415   static int belt_base_active_element[4] =
6416   {
6417     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6418     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6419     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6420     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6421   };
6422   static int belt_base_switch_element[4] =
6423   {
6424     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6425     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6426     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6427     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6428   };
6429   static int belt_move_dir[4] =
6430   {
6431     MV_LEFT,
6432     MV_NONE,
6433     MV_RIGHT,
6434     MV_NONE,
6435   };
6436
6437   int element = Feld[x][y];
6438   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6439   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6440   int belt_dir = belt_move_dir[belt_dir_nr];
6441   int xx, yy, i;
6442
6443   if (!IS_BELT_SWITCH(element))
6444     return;
6445
6446   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6447   game.belt_dir[belt_nr] = belt_dir;
6448
6449   if (belt_dir_nr == 3)
6450     belt_dir_nr = 1;
6451
6452   /* set frame order for belt animation graphic according to belt direction */
6453   for (i = 0; i < NUM_BELT_PARTS; i++)
6454   {
6455     int element = belt_base_active_element[belt_nr] + i;
6456     int graphic_1 = el2img(element);
6457     int graphic_2 = el2panelimg(element);
6458
6459     if (belt_dir == MV_LEFT)
6460     {
6461       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6462       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6463     }
6464     else
6465     {
6466       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6467       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6468     }
6469   }
6470
6471   SCAN_PLAYFIELD(xx, yy)
6472   {
6473     int element = Feld[xx][yy];
6474
6475     if (IS_BELT_SWITCH(element))
6476     {
6477       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6478
6479       if (e_belt_nr == belt_nr)
6480       {
6481         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6482         TEST_DrawLevelField(xx, yy);
6483       }
6484     }
6485     else if (IS_BELT(element) && belt_dir != MV_NONE)
6486     {
6487       int e_belt_nr = getBeltNrFromBeltElement(element);
6488
6489       if (e_belt_nr == belt_nr)
6490       {
6491         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6492
6493         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6494         TEST_DrawLevelField(xx, yy);
6495       }
6496     }
6497     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6498     {
6499       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6500
6501       if (e_belt_nr == belt_nr)
6502       {
6503         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6504
6505         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6506         TEST_DrawLevelField(xx, yy);
6507       }
6508     }
6509   }
6510 }
6511
6512 static void ToggleSwitchgateSwitch(int x, int y)
6513 {
6514   int xx, yy;
6515
6516   game.switchgate_pos = !game.switchgate_pos;
6517
6518   SCAN_PLAYFIELD(xx, yy)
6519   {
6520     int element = Feld[xx][yy];
6521
6522 #if !USE_BOTH_SWITCHGATE_SWITCHES
6523     if (element == EL_SWITCHGATE_SWITCH_UP ||
6524         element == EL_SWITCHGATE_SWITCH_DOWN)
6525     {
6526       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6527       TEST_DrawLevelField(xx, yy);
6528     }
6529     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6530              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6531     {
6532       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535 #else
6536     if (element == EL_SWITCHGATE_SWITCH_UP)
6537     {
6538       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6539       TEST_DrawLevelField(xx, yy);
6540     }
6541     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6542     {
6543       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6544       TEST_DrawLevelField(xx, yy);
6545     }
6546     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6547     {
6548       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6549       TEST_DrawLevelField(xx, yy);
6550     }
6551     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6552     {
6553       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6554       TEST_DrawLevelField(xx, yy);
6555     }
6556 #endif
6557     else if (element == EL_SWITCHGATE_OPEN ||
6558              element == EL_SWITCHGATE_OPENING)
6559     {
6560       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6561
6562       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6563     }
6564     else if (element == EL_SWITCHGATE_CLOSED ||
6565              element == EL_SWITCHGATE_CLOSING)
6566     {
6567       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6568
6569       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6570     }
6571   }
6572 }
6573
6574 static int getInvisibleActiveFromInvisibleElement(int element)
6575 {
6576   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6577           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6578           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6579           element);
6580 }
6581
6582 static int getInvisibleFromInvisibleActiveElement(int element)
6583 {
6584   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6585           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6586           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6587           element);
6588 }
6589
6590 static void RedrawAllLightSwitchesAndInvisibleElements()
6591 {
6592   int x, y;
6593
6594   SCAN_PLAYFIELD(x, y)
6595   {
6596     int element = Feld[x][y];
6597
6598     if (element == EL_LIGHT_SWITCH &&
6599         game.light_time_left > 0)
6600     {
6601       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6602       TEST_DrawLevelField(x, y);
6603     }
6604     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6605              game.light_time_left == 0)
6606     {
6607       Feld[x][y] = EL_LIGHT_SWITCH;
6608       TEST_DrawLevelField(x, y);
6609     }
6610     else if (element == EL_EMC_DRIPPER &&
6611              game.light_time_left > 0)
6612     {
6613       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6614       TEST_DrawLevelField(x, y);
6615     }
6616     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6617              game.light_time_left == 0)
6618     {
6619       Feld[x][y] = EL_EMC_DRIPPER;
6620       TEST_DrawLevelField(x, y);
6621     }
6622     else if (element == EL_INVISIBLE_STEELWALL ||
6623              element == EL_INVISIBLE_WALL ||
6624              element == EL_INVISIBLE_SAND)
6625     {
6626       if (game.light_time_left > 0)
6627         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6628
6629       TEST_DrawLevelField(x, y);
6630
6631       /* uncrumble neighbour fields, if needed */
6632       if (element == EL_INVISIBLE_SAND)
6633         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6634     }
6635     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6636              element == EL_INVISIBLE_WALL_ACTIVE ||
6637              element == EL_INVISIBLE_SAND_ACTIVE)
6638     {
6639       if (game.light_time_left == 0)
6640         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6641
6642       TEST_DrawLevelField(x, y);
6643
6644       /* re-crumble neighbour fields, if needed */
6645       if (element == EL_INVISIBLE_SAND)
6646         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6647     }
6648   }
6649 }
6650
6651 static void RedrawAllInvisibleElementsForLenses()
6652 {
6653   int x, y;
6654
6655   SCAN_PLAYFIELD(x, y)
6656   {
6657     int element = Feld[x][y];
6658
6659     if (element == EL_EMC_DRIPPER &&
6660         game.lenses_time_left > 0)
6661     {
6662       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6663       TEST_DrawLevelField(x, y);
6664     }
6665     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6666              game.lenses_time_left == 0)
6667     {
6668       Feld[x][y] = EL_EMC_DRIPPER;
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (element == EL_INVISIBLE_STEELWALL ||
6672              element == EL_INVISIBLE_WALL ||
6673              element == EL_INVISIBLE_SAND)
6674     {
6675       if (game.lenses_time_left > 0)
6676         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6677
6678       TEST_DrawLevelField(x, y);
6679
6680       /* uncrumble neighbour fields, if needed */
6681       if (element == EL_INVISIBLE_SAND)
6682         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6683     }
6684     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6685              element == EL_INVISIBLE_WALL_ACTIVE ||
6686              element == EL_INVISIBLE_SAND_ACTIVE)
6687     {
6688       if (game.lenses_time_left == 0)
6689         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6690
6691       TEST_DrawLevelField(x, y);
6692
6693       /* re-crumble neighbour fields, if needed */
6694       if (element == EL_INVISIBLE_SAND)
6695         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6696     }
6697   }
6698 }
6699
6700 static void RedrawAllInvisibleElementsForMagnifier()
6701 {
6702   int x, y;
6703
6704   SCAN_PLAYFIELD(x, y)
6705   {
6706     int element = Feld[x][y];
6707
6708     if (element == EL_EMC_FAKE_GRASS &&
6709         game.magnify_time_left > 0)
6710     {
6711       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6712       TEST_DrawLevelField(x, y);
6713     }
6714     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6715              game.magnify_time_left == 0)
6716     {
6717       Feld[x][y] = EL_EMC_FAKE_GRASS;
6718       TEST_DrawLevelField(x, y);
6719     }
6720     else if (IS_GATE_GRAY(element) &&
6721              game.magnify_time_left > 0)
6722     {
6723       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6724                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6725                     IS_EM_GATE_GRAY(element) ?
6726                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6727                     IS_EMC_GATE_GRAY(element) ?
6728                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6729                     IS_DC_GATE_GRAY(element) ?
6730                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6731                     element);
6732       TEST_DrawLevelField(x, y);
6733     }
6734     else if (IS_GATE_GRAY_ACTIVE(element) &&
6735              game.magnify_time_left == 0)
6736     {
6737       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6738                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6739                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6740                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6741                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6742                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6743                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6744                     EL_DC_GATE_WHITE_GRAY :
6745                     element);
6746       TEST_DrawLevelField(x, y);
6747     }
6748   }
6749 }
6750
6751 static void ToggleLightSwitch(int x, int y)
6752 {
6753   int element = Feld[x][y];
6754
6755   game.light_time_left =
6756     (element == EL_LIGHT_SWITCH ?
6757      level.time_light * FRAMES_PER_SECOND : 0);
6758
6759   RedrawAllLightSwitchesAndInvisibleElements();
6760 }
6761
6762 static void ActivateTimegateSwitch(int x, int y)
6763 {
6764   int xx, yy;
6765
6766   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6767
6768   SCAN_PLAYFIELD(xx, yy)
6769   {
6770     int element = Feld[xx][yy];
6771
6772     if (element == EL_TIMEGATE_CLOSED ||
6773         element == EL_TIMEGATE_CLOSING)
6774     {
6775       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6776       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6777     }
6778
6779     /*
6780     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6781     {
6782       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6783       TEST_DrawLevelField(xx, yy);
6784     }
6785     */
6786
6787   }
6788
6789 #if 1
6790   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6791                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6792 #else
6793   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6794 #endif
6795 }
6796
6797 void Impact(int x, int y)
6798 {
6799   boolean last_line = (y == lev_fieldy - 1);
6800   boolean object_hit = FALSE;
6801   boolean impact = (last_line || object_hit);
6802   int element = Feld[x][y];
6803   int smashed = EL_STEELWALL;
6804
6805   if (!last_line)       /* check if element below was hit */
6806   {
6807     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6808       return;
6809
6810     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6811                                          MovDir[x][y + 1] != MV_DOWN ||
6812                                          MovPos[x][y + 1] <= TILEY / 2));
6813
6814     /* do not smash moving elements that left the smashed field in time */
6815     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6816         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6817       object_hit = FALSE;
6818
6819 #if USE_QUICKSAND_IMPACT_BUGFIX
6820     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6821     {
6822       RemoveMovingField(x, y + 1);
6823       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6824       Feld[x][y + 2] = EL_ROCK;
6825       TEST_DrawLevelField(x, y + 2);
6826
6827       object_hit = TRUE;
6828     }
6829
6830     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6831     {
6832       RemoveMovingField(x, y + 1);
6833       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6834       Feld[x][y + 2] = EL_ROCK;
6835       TEST_DrawLevelField(x, y + 2);
6836
6837       object_hit = TRUE;
6838     }
6839 #endif
6840
6841     if (object_hit)
6842       smashed = MovingOrBlocked2Element(x, y + 1);
6843
6844     impact = (last_line || object_hit);
6845   }
6846
6847   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6848   {
6849     SplashAcid(x, y + 1);
6850     return;
6851   }
6852
6853   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6854   /* only reset graphic animation if graphic really changes after impact */
6855   if (impact &&
6856       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6857   {
6858     ResetGfxAnimation(x, y);
6859     TEST_DrawLevelField(x, y);
6860   }
6861
6862   if (impact && CAN_EXPLODE_IMPACT(element))
6863   {
6864     Bang(x, y);
6865     return;
6866   }
6867   else if (impact && element == EL_PEARL &&
6868            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6869   {
6870     ResetGfxAnimation(x, y);
6871
6872     Feld[x][y] = EL_PEARL_BREAKING;
6873     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6874     return;
6875   }
6876   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6877   {
6878     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6879
6880     return;
6881   }
6882
6883   if (impact && element == EL_AMOEBA_DROP)
6884   {
6885     if (object_hit && IS_PLAYER(x, y + 1))
6886       KillPlayerUnlessEnemyProtected(x, y + 1);
6887     else if (object_hit && smashed == EL_PENGUIN)
6888       Bang(x, y + 1);
6889     else
6890     {
6891       Feld[x][y] = EL_AMOEBA_GROWING;
6892       Store[x][y] = EL_AMOEBA_WET;
6893
6894       ResetRandomAnimationValue(x, y);
6895     }
6896     return;
6897   }
6898
6899   if (object_hit)               /* check which object was hit */
6900   {
6901     if ((CAN_PASS_MAGIC_WALL(element) && 
6902          (smashed == EL_MAGIC_WALL ||
6903           smashed == EL_BD_MAGIC_WALL)) ||
6904         (CAN_PASS_DC_MAGIC_WALL(element) &&
6905          smashed == EL_DC_MAGIC_WALL))
6906     {
6907       int xx, yy;
6908       int activated_magic_wall =
6909         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6910          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6911          EL_DC_MAGIC_WALL_ACTIVE);
6912
6913       /* activate magic wall / mill */
6914       SCAN_PLAYFIELD(xx, yy)
6915       {
6916         if (Feld[xx][yy] == smashed)
6917           Feld[xx][yy] = activated_magic_wall;
6918       }
6919
6920       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6921       game.magic_wall_active = TRUE;
6922
6923       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6924                             SND_MAGIC_WALL_ACTIVATING :
6925                             smashed == EL_BD_MAGIC_WALL ?
6926                             SND_BD_MAGIC_WALL_ACTIVATING :
6927                             SND_DC_MAGIC_WALL_ACTIVATING));
6928     }
6929
6930     if (IS_PLAYER(x, y + 1))
6931     {
6932       if (CAN_SMASH_PLAYER(element))
6933       {
6934         KillPlayerUnlessEnemyProtected(x, y + 1);
6935         return;
6936       }
6937     }
6938     else if (smashed == EL_PENGUIN)
6939     {
6940       if (CAN_SMASH_PLAYER(element))
6941       {
6942         Bang(x, y + 1);
6943         return;
6944       }
6945     }
6946     else if (element == EL_BD_DIAMOND)
6947     {
6948       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6949       {
6950         Bang(x, y + 1);
6951         return;
6952       }
6953     }
6954     else if (((element == EL_SP_INFOTRON ||
6955                element == EL_SP_ZONK) &&
6956               (smashed == EL_SP_SNIKSNAK ||
6957                smashed == EL_SP_ELECTRON ||
6958                smashed == EL_SP_DISK_ORANGE)) ||
6959              (element == EL_SP_INFOTRON &&
6960               smashed == EL_SP_DISK_YELLOW))
6961     {
6962       Bang(x, y + 1);
6963       return;
6964     }
6965     else if (CAN_SMASH_EVERYTHING(element))
6966     {
6967       if (IS_CLASSIC_ENEMY(smashed) ||
6968           CAN_EXPLODE_SMASHED(smashed))
6969       {
6970         Bang(x, y + 1);
6971         return;
6972       }
6973       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6974       {
6975         if (smashed == EL_LAMP ||
6976             smashed == EL_LAMP_ACTIVE)
6977         {
6978           Bang(x, y + 1);
6979           return;
6980         }
6981         else if (smashed == EL_NUT)
6982         {
6983           Feld[x][y + 1] = EL_NUT_BREAKING;
6984           PlayLevelSound(x, y, SND_NUT_BREAKING);
6985           RaiseScoreElement(EL_NUT);
6986           return;
6987         }
6988         else if (smashed == EL_PEARL)
6989         {
6990           ResetGfxAnimation(x, y);
6991
6992           Feld[x][y + 1] = EL_PEARL_BREAKING;
6993           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6994           return;
6995         }
6996         else if (smashed == EL_DIAMOND)
6997         {
6998           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6999           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7000           return;
7001         }
7002         else if (IS_BELT_SWITCH(smashed))
7003         {
7004           ToggleBeltSwitch(x, y + 1);
7005         }
7006         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7007                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7008                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7009                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7010         {
7011           ToggleSwitchgateSwitch(x, y + 1);
7012         }
7013         else if (smashed == EL_LIGHT_SWITCH ||
7014                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7015         {
7016           ToggleLightSwitch(x, y + 1);
7017         }
7018         else
7019         {
7020 #if 0
7021           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7022 #endif
7023
7024           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7025
7026           CheckElementChangeBySide(x, y + 1, smashed, element,
7027                                    CE_SWITCHED, CH_SIDE_TOP);
7028           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7029                                             CH_SIDE_TOP);
7030         }
7031       }
7032       else
7033       {
7034         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7035       }
7036     }
7037   }
7038
7039   /* play sound of magic wall / mill */
7040   if (!last_line &&
7041       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7042        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7043        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7044   {
7045     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7046       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7047     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7048       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7049     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7050       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7051
7052     return;
7053   }
7054
7055   /* play sound of object that hits the ground */
7056   if (last_line || object_hit)
7057     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7058 }
7059
7060 inline static void TurnRoundExt(int x, int y)
7061 {
7062   static struct
7063   {
7064     int dx, dy;
7065   } move_xy[] =
7066   {
7067     {  0,  0 },
7068     { -1,  0 },
7069     { +1,  0 },
7070     {  0,  0 },
7071     {  0, -1 },
7072     {  0,  0 }, { 0, 0 }, { 0, 0 },
7073     {  0, +1 }
7074   };
7075   static struct
7076   {
7077     int left, right, back;
7078   } turn[] =
7079   {
7080     { 0,        0,              0        },
7081     { MV_DOWN,  MV_UP,          MV_RIGHT },
7082     { MV_UP,    MV_DOWN,        MV_LEFT  },
7083     { 0,        0,              0        },
7084     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7085     { 0,        0,              0        },
7086     { 0,        0,              0        },
7087     { 0,        0,              0        },
7088     { MV_RIGHT, MV_LEFT,        MV_UP    }
7089   };
7090
7091   int element = Feld[x][y];
7092   int move_pattern = element_info[element].move_pattern;
7093
7094   int old_move_dir = MovDir[x][y];
7095   int left_dir  = turn[old_move_dir].left;
7096   int right_dir = turn[old_move_dir].right;
7097   int back_dir  = turn[old_move_dir].back;
7098
7099   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7100   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7101   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7102   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7103
7104   int left_x  = x + left_dx,  left_y  = y + left_dy;
7105   int right_x = x + right_dx, right_y = y + right_dy;
7106   int move_x  = x + move_dx,  move_y  = y + move_dy;
7107
7108   int xx, yy;
7109
7110   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7111   {
7112     TestIfBadThingTouchesOtherBadThing(x, y);
7113
7114     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7115       MovDir[x][y] = right_dir;
7116     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7117       MovDir[x][y] = left_dir;
7118
7119     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7120       MovDelay[x][y] = 9;
7121     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7122       MovDelay[x][y] = 1;
7123   }
7124   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7125   {
7126     TestIfBadThingTouchesOtherBadThing(x, y);
7127
7128     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7129       MovDir[x][y] = left_dir;
7130     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7131       MovDir[x][y] = right_dir;
7132
7133     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7134       MovDelay[x][y] = 9;
7135     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7136       MovDelay[x][y] = 1;
7137   }
7138   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7139   {
7140     TestIfBadThingTouchesOtherBadThing(x, y);
7141
7142     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7143       MovDir[x][y] = left_dir;
7144     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7145       MovDir[x][y] = right_dir;
7146
7147     if (MovDir[x][y] != old_move_dir)
7148       MovDelay[x][y] = 9;
7149   }
7150   else if (element == EL_YAMYAM)
7151   {
7152     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7153     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7154
7155     if (can_turn_left && can_turn_right)
7156       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7157     else if (can_turn_left)
7158       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7159     else if (can_turn_right)
7160       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7161     else
7162       MovDir[x][y] = back_dir;
7163
7164     MovDelay[x][y] = 16 + 16 * RND(3);
7165   }
7166   else if (element == EL_DARK_YAMYAM)
7167   {
7168     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7169                                                          left_x, left_y);
7170     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7171                                                          right_x, right_y);
7172
7173     if (can_turn_left && can_turn_right)
7174       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7175     else if (can_turn_left)
7176       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7177     else if (can_turn_right)
7178       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7179     else
7180       MovDir[x][y] = back_dir;
7181
7182     MovDelay[x][y] = 16 + 16 * RND(3);
7183   }
7184   else if (element == EL_PACMAN)
7185   {
7186     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7187     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7188
7189     if (can_turn_left && can_turn_right)
7190       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7191     else if (can_turn_left)
7192       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7193     else if (can_turn_right)
7194       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7195     else
7196       MovDir[x][y] = back_dir;
7197
7198     MovDelay[x][y] = 6 + RND(40);
7199   }
7200   else if (element == EL_PIG)
7201   {
7202     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7203     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7204     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7205     boolean should_turn_left, should_turn_right, should_move_on;
7206     int rnd_value = 24;
7207     int rnd = RND(rnd_value);
7208
7209     should_turn_left = (can_turn_left &&
7210                         (!can_move_on ||
7211                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7212                                                    y + back_dy + left_dy)));
7213     should_turn_right = (can_turn_right &&
7214                          (!can_move_on ||
7215                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7216                                                     y + back_dy + right_dy)));
7217     should_move_on = (can_move_on &&
7218                       (!can_turn_left ||
7219                        !can_turn_right ||
7220                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7221                                                  y + move_dy + left_dy) ||
7222                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7223                                                  y + move_dy + right_dy)));
7224
7225     if (should_turn_left || should_turn_right || should_move_on)
7226     {
7227       if (should_turn_left && should_turn_right && should_move_on)
7228         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7229                         rnd < 2 * rnd_value / 3 ? right_dir :
7230                         old_move_dir);
7231       else if (should_turn_left && should_turn_right)
7232         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7233       else if (should_turn_left && should_move_on)
7234         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7235       else if (should_turn_right && should_move_on)
7236         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7237       else if (should_turn_left)
7238         MovDir[x][y] = left_dir;
7239       else if (should_turn_right)
7240         MovDir[x][y] = right_dir;
7241       else if (should_move_on)
7242         MovDir[x][y] = old_move_dir;
7243     }
7244     else if (can_move_on && rnd > rnd_value / 8)
7245       MovDir[x][y] = old_move_dir;
7246     else if (can_turn_left && can_turn_right)
7247       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7248     else if (can_turn_left && rnd > rnd_value / 8)
7249       MovDir[x][y] = left_dir;
7250     else if (can_turn_right && rnd > rnd_value/8)
7251       MovDir[x][y] = right_dir;
7252     else
7253       MovDir[x][y] = back_dir;
7254
7255     xx = x + move_xy[MovDir[x][y]].dx;
7256     yy = y + move_xy[MovDir[x][y]].dy;
7257
7258     if (!IN_LEV_FIELD(xx, yy) ||
7259         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7260       MovDir[x][y] = old_move_dir;
7261
7262     MovDelay[x][y] = 0;
7263   }
7264   else if (element == EL_DRAGON)
7265   {
7266     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7267     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7268     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7269     int rnd_value = 24;
7270     int rnd = RND(rnd_value);
7271
7272     if (can_move_on && rnd > rnd_value / 8)
7273       MovDir[x][y] = old_move_dir;
7274     else if (can_turn_left && can_turn_right)
7275       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7276     else if (can_turn_left && rnd > rnd_value / 8)
7277       MovDir[x][y] = left_dir;
7278     else if (can_turn_right && rnd > rnd_value / 8)
7279       MovDir[x][y] = right_dir;
7280     else
7281       MovDir[x][y] = back_dir;
7282
7283     xx = x + move_xy[MovDir[x][y]].dx;
7284     yy = y + move_xy[MovDir[x][y]].dy;
7285
7286     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7287       MovDir[x][y] = old_move_dir;
7288
7289     MovDelay[x][y] = 0;
7290   }
7291   else if (element == EL_MOLE)
7292   {
7293     boolean can_move_on =
7294       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7295                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7296                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7297     if (!can_move_on)
7298     {
7299       boolean can_turn_left =
7300         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7301                               IS_AMOEBOID(Feld[left_x][left_y])));
7302
7303       boolean can_turn_right =
7304         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7305                               IS_AMOEBOID(Feld[right_x][right_y])));
7306
7307       if (can_turn_left && can_turn_right)
7308         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7309       else if (can_turn_left)
7310         MovDir[x][y] = left_dir;
7311       else
7312         MovDir[x][y] = right_dir;
7313     }
7314
7315     if (MovDir[x][y] != old_move_dir)
7316       MovDelay[x][y] = 9;
7317   }
7318   else if (element == EL_BALLOON)
7319   {
7320     MovDir[x][y] = game.wind_direction;
7321     MovDelay[x][y] = 0;
7322   }
7323   else if (element == EL_SPRING)
7324   {
7325 #if USE_NEW_SPRING_BUMPER
7326     if (MovDir[x][y] & MV_HORIZONTAL)
7327     {
7328       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7329           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7330       {
7331         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7332         ResetGfxAnimation(move_x, move_y);
7333         TEST_DrawLevelField(move_x, move_y);
7334
7335         MovDir[x][y] = back_dir;
7336       }
7337       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7338                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7339         MovDir[x][y] = MV_NONE;
7340     }
7341 #else
7342     if (MovDir[x][y] & MV_HORIZONTAL &&
7343         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7344          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7345       MovDir[x][y] = MV_NONE;
7346 #endif
7347
7348     MovDelay[x][y] = 0;
7349   }
7350   else if (element == EL_ROBOT ||
7351            element == EL_SATELLITE ||
7352            element == EL_PENGUIN ||
7353            element == EL_EMC_ANDROID)
7354   {
7355     int attr_x = -1, attr_y = -1;
7356
7357     if (AllPlayersGone)
7358     {
7359       attr_x = ExitX;
7360       attr_y = ExitY;
7361     }
7362     else
7363     {
7364       int i;
7365
7366       for (i = 0; i < MAX_PLAYERS; i++)
7367       {
7368         struct PlayerInfo *player = &stored_player[i];
7369         int jx = player->jx, jy = player->jy;
7370
7371         if (!player->active)
7372           continue;
7373
7374         if (attr_x == -1 ||
7375             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7376         {
7377           attr_x = jx;
7378           attr_y = jy;
7379         }
7380       }
7381     }
7382
7383     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7384         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7385          game.engine_version < VERSION_IDENT(3,1,0,0)))
7386     {
7387       attr_x = ZX;
7388       attr_y = ZY;
7389     }
7390
7391     if (element == EL_PENGUIN)
7392     {
7393       int i;
7394       static int xy[4][2] =
7395       {
7396         { 0, -1 },
7397         { -1, 0 },
7398         { +1, 0 },
7399         { 0, +1 }
7400       };
7401
7402       for (i = 0; i < NUM_DIRECTIONS; i++)
7403       {
7404         int ex = x + xy[i][0];
7405         int ey = y + xy[i][1];
7406
7407         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7408                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7409                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7410                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7411         {
7412           attr_x = ex;
7413           attr_y = ey;
7414           break;
7415         }
7416       }
7417     }
7418
7419     MovDir[x][y] = MV_NONE;
7420     if (attr_x < x)
7421       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7422     else if (attr_x > x)
7423       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7424     if (attr_y < y)
7425       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7426     else if (attr_y > y)
7427       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7428
7429     if (element == EL_ROBOT)
7430     {
7431       int newx, newy;
7432
7433       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7434         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7435       Moving2Blocked(x, y, &newx, &newy);
7436
7437       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7438         MovDelay[x][y] = 8 + 8 * !RND(3);
7439       else
7440         MovDelay[x][y] = 16;
7441     }
7442     else if (element == EL_PENGUIN)
7443     {
7444       int newx, newy;
7445
7446       MovDelay[x][y] = 1;
7447
7448       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7449       {
7450         boolean first_horiz = RND(2);
7451         int new_move_dir = MovDir[x][y];
7452
7453         MovDir[x][y] =
7454           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7455         Moving2Blocked(x, y, &newx, &newy);
7456
7457         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7458           return;
7459
7460         MovDir[x][y] =
7461           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7462         Moving2Blocked(x, y, &newx, &newy);
7463
7464         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7465           return;
7466
7467         MovDir[x][y] = old_move_dir;
7468         return;
7469       }
7470     }
7471     else if (element == EL_SATELLITE)
7472     {
7473       int newx, newy;
7474
7475       MovDelay[x][y] = 1;
7476
7477       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7478       {
7479         boolean first_horiz = RND(2);
7480         int new_move_dir = MovDir[x][y];
7481
7482         MovDir[x][y] =
7483           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7484         Moving2Blocked(x, y, &newx, &newy);
7485
7486         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7487           return;
7488
7489         MovDir[x][y] =
7490           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7491         Moving2Blocked(x, y, &newx, &newy);
7492
7493         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7494           return;
7495
7496         MovDir[x][y] = old_move_dir;
7497         return;
7498       }
7499     }
7500     else if (element == EL_EMC_ANDROID)
7501     {
7502       static int check_pos[16] =
7503       {
7504         -1,             /*  0 => (invalid)          */
7505         7,              /*  1 => MV_LEFT            */
7506         3,              /*  2 => MV_RIGHT           */
7507         -1,             /*  3 => (invalid)          */
7508         1,              /*  4 =>            MV_UP   */
7509         0,              /*  5 => MV_LEFT  | MV_UP   */
7510         2,              /*  6 => MV_RIGHT | MV_UP   */
7511         -1,             /*  7 => (invalid)          */
7512         5,              /*  8 =>            MV_DOWN */
7513         6,              /*  9 => MV_LEFT  | MV_DOWN */
7514         4,              /* 10 => MV_RIGHT | MV_DOWN */
7515         -1,             /* 11 => (invalid)          */
7516         -1,             /* 12 => (invalid)          */
7517         -1,             /* 13 => (invalid)          */
7518         -1,             /* 14 => (invalid)          */
7519         -1,             /* 15 => (invalid)          */
7520       };
7521       static struct
7522       {
7523         int dx, dy;
7524         int dir;
7525       } check_xy[8] =
7526       {
7527         { -1, -1,       MV_LEFT  | MV_UP   },
7528         {  0, -1,                  MV_UP   },
7529         { +1, -1,       MV_RIGHT | MV_UP   },
7530         { +1,  0,       MV_RIGHT           },
7531         { +1, +1,       MV_RIGHT | MV_DOWN },
7532         {  0, +1,                  MV_DOWN },
7533         { -1, +1,       MV_LEFT  | MV_DOWN },
7534         { -1,  0,       MV_LEFT            },
7535       };
7536       int start_pos, check_order;
7537       boolean can_clone = FALSE;
7538       int i;
7539
7540       /* check if there is any free field around current position */
7541       for (i = 0; i < 8; i++)
7542       {
7543         int newx = x + check_xy[i].dx;
7544         int newy = y + check_xy[i].dy;
7545
7546         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7547         {
7548           can_clone = TRUE;
7549
7550           break;
7551         }
7552       }
7553
7554       if (can_clone)            /* randomly find an element to clone */
7555       {
7556         can_clone = FALSE;
7557
7558         start_pos = check_pos[RND(8)];
7559         check_order = (RND(2) ? -1 : +1);
7560
7561         for (i = 0; i < 8; i++)
7562         {
7563           int pos_raw = start_pos + i * check_order;
7564           int pos = (pos_raw + 8) % 8;
7565           int newx = x + check_xy[pos].dx;
7566           int newy = y + check_xy[pos].dy;
7567
7568           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7569           {
7570             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7571             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7572
7573             Store[x][y] = Feld[newx][newy];
7574
7575             can_clone = TRUE;
7576
7577             break;
7578           }
7579         }
7580       }
7581
7582       if (can_clone)            /* randomly find a direction to move */
7583       {
7584         can_clone = FALSE;
7585
7586         start_pos = check_pos[RND(8)];
7587         check_order = (RND(2) ? -1 : +1);
7588
7589         for (i = 0; i < 8; i++)
7590         {
7591           int pos_raw = start_pos + i * check_order;
7592           int pos = (pos_raw + 8) % 8;
7593           int newx = x + check_xy[pos].dx;
7594           int newy = y + check_xy[pos].dy;
7595           int new_move_dir = check_xy[pos].dir;
7596
7597           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7598           {
7599             MovDir[x][y] = new_move_dir;
7600             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7601
7602             can_clone = TRUE;
7603
7604             break;
7605           }
7606         }
7607       }
7608
7609       if (can_clone)            /* cloning and moving successful */
7610         return;
7611
7612       /* cannot clone -- try to move towards player */
7613
7614       start_pos = check_pos[MovDir[x][y] & 0x0f];
7615       check_order = (RND(2) ? -1 : +1);
7616
7617       for (i = 0; i < 3; i++)
7618       {
7619         /* first check start_pos, then previous/next or (next/previous) pos */
7620         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7621         int pos = (pos_raw + 8) % 8;
7622         int newx = x + check_xy[pos].dx;
7623         int newy = y + check_xy[pos].dy;
7624         int new_move_dir = check_xy[pos].dir;
7625
7626         if (IS_PLAYER(newx, newy))
7627           break;
7628
7629         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7630         {
7631           MovDir[x][y] = new_move_dir;
7632           MovDelay[x][y] = level.android_move_time * 8 + 1;
7633
7634           break;
7635         }
7636       }
7637     }
7638   }
7639   else if (move_pattern == MV_TURNING_LEFT ||
7640            move_pattern == MV_TURNING_RIGHT ||
7641            move_pattern == MV_TURNING_LEFT_RIGHT ||
7642            move_pattern == MV_TURNING_RIGHT_LEFT ||
7643            move_pattern == MV_TURNING_RANDOM ||
7644            move_pattern == MV_ALL_DIRECTIONS)
7645   {
7646     boolean can_turn_left =
7647       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7648     boolean can_turn_right =
7649       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7650
7651     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7652       return;
7653
7654     if (move_pattern == MV_TURNING_LEFT)
7655       MovDir[x][y] = left_dir;
7656     else if (move_pattern == MV_TURNING_RIGHT)
7657       MovDir[x][y] = right_dir;
7658     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7659       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7660     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7661       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7662     else if (move_pattern == MV_TURNING_RANDOM)
7663       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7664                       can_turn_right && !can_turn_left ? right_dir :
7665                       RND(2) ? left_dir : right_dir);
7666     else if (can_turn_left && can_turn_right)
7667       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7668     else if (can_turn_left)
7669       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7670     else if (can_turn_right)
7671       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7672     else
7673       MovDir[x][y] = back_dir;
7674
7675     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7676   }
7677   else if (move_pattern == MV_HORIZONTAL ||
7678            move_pattern == MV_VERTICAL)
7679   {
7680     if (move_pattern & old_move_dir)
7681       MovDir[x][y] = back_dir;
7682     else if (move_pattern == MV_HORIZONTAL)
7683       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7684     else if (move_pattern == MV_VERTICAL)
7685       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7686
7687     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7688   }
7689   else if (move_pattern & MV_ANY_DIRECTION)
7690   {
7691     MovDir[x][y] = move_pattern;
7692     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7693   }
7694   else if (move_pattern & MV_WIND_DIRECTION)
7695   {
7696     MovDir[x][y] = game.wind_direction;
7697     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7698   }
7699   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7700   {
7701     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7702       MovDir[x][y] = left_dir;
7703     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7704       MovDir[x][y] = right_dir;
7705
7706     if (MovDir[x][y] != old_move_dir)
7707       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7708   }
7709   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7710   {
7711     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7712       MovDir[x][y] = right_dir;
7713     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7714       MovDir[x][y] = left_dir;
7715
7716     if (MovDir[x][y] != old_move_dir)
7717       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7718   }
7719   else if (move_pattern == MV_TOWARDS_PLAYER ||
7720            move_pattern == MV_AWAY_FROM_PLAYER)
7721   {
7722     int attr_x = -1, attr_y = -1;
7723     int newx, newy;
7724     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7725
7726     if (AllPlayersGone)
7727     {
7728       attr_x = ExitX;
7729       attr_y = ExitY;
7730     }
7731     else
7732     {
7733       int i;
7734
7735       for (i = 0; i < MAX_PLAYERS; i++)
7736       {
7737         struct PlayerInfo *player = &stored_player[i];
7738         int jx = player->jx, jy = player->jy;
7739
7740         if (!player->active)
7741           continue;
7742
7743         if (attr_x == -1 ||
7744             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7745         {
7746           attr_x = jx;
7747           attr_y = jy;
7748         }
7749       }
7750     }
7751
7752     MovDir[x][y] = MV_NONE;
7753     if (attr_x < x)
7754       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7755     else if (attr_x > x)
7756       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7757     if (attr_y < y)
7758       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7759     else if (attr_y > y)
7760       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7761
7762     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7763
7764     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7765     {
7766       boolean first_horiz = RND(2);
7767       int new_move_dir = MovDir[x][y];
7768
7769       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7770       {
7771         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7772         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7773
7774         return;
7775       }
7776
7777       MovDir[x][y] =
7778         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7779       Moving2Blocked(x, y, &newx, &newy);
7780
7781       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7782         return;
7783
7784       MovDir[x][y] =
7785         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7786       Moving2Blocked(x, y, &newx, &newy);
7787
7788       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7789         return;
7790
7791       MovDir[x][y] = old_move_dir;
7792     }
7793   }
7794   else if (move_pattern == MV_WHEN_PUSHED ||
7795            move_pattern == MV_WHEN_DROPPED)
7796   {
7797     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7798       MovDir[x][y] = MV_NONE;
7799
7800     MovDelay[x][y] = 0;
7801   }
7802   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7803   {
7804     static int test_xy[7][2] =
7805     {
7806       { 0, -1 },
7807       { -1, 0 },
7808       { +1, 0 },
7809       { 0, +1 },
7810       { 0, -1 },
7811       { -1, 0 },
7812       { +1, 0 },
7813     };
7814     static int test_dir[7] =
7815     {
7816       MV_UP,
7817       MV_LEFT,
7818       MV_RIGHT,
7819       MV_DOWN,
7820       MV_UP,
7821       MV_LEFT,
7822       MV_RIGHT,
7823     };
7824     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7825     int move_preference = -1000000;     /* start with very low preference */
7826     int new_move_dir = MV_NONE;
7827     int start_test = RND(4);
7828     int i;
7829
7830     for (i = 0; i < NUM_DIRECTIONS; i++)
7831     {
7832       int move_dir = test_dir[start_test + i];
7833       int move_dir_preference;
7834
7835       xx = x + test_xy[start_test + i][0];
7836       yy = y + test_xy[start_test + i][1];
7837
7838       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7839           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7840       {
7841         new_move_dir = move_dir;
7842
7843         break;
7844       }
7845
7846       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7847         continue;
7848
7849       move_dir_preference = -1 * RunnerVisit[xx][yy];
7850       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7851         move_dir_preference = PlayerVisit[xx][yy];
7852
7853       if (move_dir_preference > move_preference)
7854       {
7855         /* prefer field that has not been visited for the longest time */
7856         move_preference = move_dir_preference;
7857         new_move_dir = move_dir;
7858       }
7859       else if (move_dir_preference == move_preference &&
7860                move_dir == old_move_dir)
7861       {
7862         /* prefer last direction when all directions are preferred equally */
7863         move_preference = move_dir_preference;
7864         new_move_dir = move_dir;
7865       }
7866     }
7867
7868     MovDir[x][y] = new_move_dir;
7869     if (old_move_dir != new_move_dir)
7870       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7871   }
7872 }
7873
7874 static void TurnRound(int x, int y)
7875 {
7876   int direction = MovDir[x][y];
7877
7878   TurnRoundExt(x, y);
7879
7880   GfxDir[x][y] = MovDir[x][y];
7881
7882   if (direction != MovDir[x][y])
7883     GfxFrame[x][y] = 0;
7884
7885   if (MovDelay[x][y])
7886     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7887
7888   ResetGfxFrame(x, y, FALSE);
7889 }
7890
7891 static boolean JustBeingPushed(int x, int y)
7892 {
7893   int i;
7894
7895   for (i = 0; i < MAX_PLAYERS; i++)
7896   {
7897     struct PlayerInfo *player = &stored_player[i];
7898
7899     if (player->active && player->is_pushing && player->MovPos)
7900     {
7901       int next_jx = player->jx + (player->jx - player->last_jx);
7902       int next_jy = player->jy + (player->jy - player->last_jy);
7903
7904       if (x == next_jx && y == next_jy)
7905         return TRUE;
7906     }
7907   }
7908
7909   return FALSE;
7910 }
7911
7912 void StartMoving(int x, int y)
7913 {
7914   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7915   int element = Feld[x][y];
7916
7917   if (Stop[x][y])
7918     return;
7919
7920   if (MovDelay[x][y] == 0)
7921     GfxAction[x][y] = ACTION_DEFAULT;
7922
7923   if (CAN_FALL(element) && y < lev_fieldy - 1)
7924   {
7925     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7926         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7927       if (JustBeingPushed(x, y))
7928         return;
7929
7930     if (element == EL_QUICKSAND_FULL)
7931     {
7932       if (IS_FREE(x, y + 1))
7933       {
7934         InitMovingField(x, y, MV_DOWN);
7935         started_moving = TRUE;
7936
7937         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7938 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7939         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7940           Store[x][y] = EL_ROCK;
7941 #else
7942         Store[x][y] = EL_ROCK;
7943 #endif
7944
7945         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7946       }
7947       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7948       {
7949         if (!MovDelay[x][y])
7950         {
7951           MovDelay[x][y] = TILEY + 1;
7952
7953           ResetGfxAnimation(x, y);
7954           ResetGfxAnimation(x, y + 1);
7955         }
7956
7957         if (MovDelay[x][y])
7958         {
7959           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7960           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7961
7962           MovDelay[x][y]--;
7963           if (MovDelay[x][y])
7964             return;
7965         }
7966
7967         Feld[x][y] = EL_QUICKSAND_EMPTY;
7968         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7969         Store[x][y + 1] = Store[x][y];
7970         Store[x][y] = 0;
7971
7972         PlayLevelSoundAction(x, y, ACTION_FILLING);
7973       }
7974       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7975       {
7976         if (!MovDelay[x][y])
7977         {
7978           MovDelay[x][y] = TILEY + 1;
7979
7980           ResetGfxAnimation(x, y);
7981           ResetGfxAnimation(x, y + 1);
7982         }
7983
7984         if (MovDelay[x][y])
7985         {
7986           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7987           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7988
7989           MovDelay[x][y]--;
7990           if (MovDelay[x][y])
7991             return;
7992         }
7993
7994         Feld[x][y] = EL_QUICKSAND_EMPTY;
7995         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7996         Store[x][y + 1] = Store[x][y];
7997         Store[x][y] = 0;
7998
7999         PlayLevelSoundAction(x, y, ACTION_FILLING);
8000       }
8001     }
8002     else if (element == EL_QUICKSAND_FAST_FULL)
8003     {
8004       if (IS_FREE(x, y + 1))
8005       {
8006         InitMovingField(x, y, MV_DOWN);
8007         started_moving = TRUE;
8008
8009         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8010 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8011         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8012           Store[x][y] = EL_ROCK;
8013 #else
8014         Store[x][y] = EL_ROCK;
8015 #endif
8016
8017         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8018       }
8019       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8020       {
8021         if (!MovDelay[x][y])
8022         {
8023           MovDelay[x][y] = TILEY + 1;
8024
8025           ResetGfxAnimation(x, y);
8026           ResetGfxAnimation(x, y + 1);
8027         }
8028
8029         if (MovDelay[x][y])
8030         {
8031           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8032           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8033
8034           MovDelay[x][y]--;
8035           if (MovDelay[x][y])
8036             return;
8037         }
8038
8039         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8040         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8041         Store[x][y + 1] = Store[x][y];
8042         Store[x][y] = 0;
8043
8044         PlayLevelSoundAction(x, y, ACTION_FILLING);
8045       }
8046       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8047       {
8048         if (!MovDelay[x][y])
8049         {
8050           MovDelay[x][y] = TILEY + 1;
8051
8052           ResetGfxAnimation(x, y);
8053           ResetGfxAnimation(x, y + 1);
8054         }
8055
8056         if (MovDelay[x][y])
8057         {
8058           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8059           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8060
8061           MovDelay[x][y]--;
8062           if (MovDelay[x][y])
8063             return;
8064         }
8065
8066         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8067         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8068         Store[x][y + 1] = Store[x][y];
8069         Store[x][y] = 0;
8070
8071         PlayLevelSoundAction(x, y, ACTION_FILLING);
8072       }
8073     }
8074     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8075              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8076     {
8077       InitMovingField(x, y, MV_DOWN);
8078       started_moving = TRUE;
8079
8080       Feld[x][y] = EL_QUICKSAND_FILLING;
8081       Store[x][y] = element;
8082
8083       PlayLevelSoundAction(x, y, ACTION_FILLING);
8084     }
8085     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8086              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8087     {
8088       InitMovingField(x, y, MV_DOWN);
8089       started_moving = TRUE;
8090
8091       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8092       Store[x][y] = element;
8093
8094       PlayLevelSoundAction(x, y, ACTION_FILLING);
8095     }
8096     else if (element == EL_MAGIC_WALL_FULL)
8097     {
8098       if (IS_FREE(x, y + 1))
8099       {
8100         InitMovingField(x, y, MV_DOWN);
8101         started_moving = TRUE;
8102
8103         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8104         Store[x][y] = EL_CHANGED(Store[x][y]);
8105       }
8106       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8107       {
8108         if (!MovDelay[x][y])
8109           MovDelay[x][y] = TILEY/4 + 1;
8110
8111         if (MovDelay[x][y])
8112         {
8113           MovDelay[x][y]--;
8114           if (MovDelay[x][y])
8115             return;
8116         }
8117
8118         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8119         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8120         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8121         Store[x][y] = 0;
8122       }
8123     }
8124     else if (element == EL_BD_MAGIC_WALL_FULL)
8125     {
8126       if (IS_FREE(x, y + 1))
8127       {
8128         InitMovingField(x, y, MV_DOWN);
8129         started_moving = TRUE;
8130
8131         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8132         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8133       }
8134       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8135       {
8136         if (!MovDelay[x][y])
8137           MovDelay[x][y] = TILEY/4 + 1;
8138
8139         if (MovDelay[x][y])
8140         {
8141           MovDelay[x][y]--;
8142           if (MovDelay[x][y])
8143             return;
8144         }
8145
8146         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8147         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8148         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8149         Store[x][y] = 0;
8150       }
8151     }
8152     else if (element == EL_DC_MAGIC_WALL_FULL)
8153     {
8154       if (IS_FREE(x, y + 1))
8155       {
8156         InitMovingField(x, y, MV_DOWN);
8157         started_moving = TRUE;
8158
8159         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8160         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8161       }
8162       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8163       {
8164         if (!MovDelay[x][y])
8165           MovDelay[x][y] = TILEY/4 + 1;
8166
8167         if (MovDelay[x][y])
8168         {
8169           MovDelay[x][y]--;
8170           if (MovDelay[x][y])
8171             return;
8172         }
8173
8174         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8175         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8176         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8177         Store[x][y] = 0;
8178       }
8179     }
8180     else if ((CAN_PASS_MAGIC_WALL(element) &&
8181               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8182                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8183              (CAN_PASS_DC_MAGIC_WALL(element) &&
8184               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8185
8186     {
8187       InitMovingField(x, y, MV_DOWN);
8188       started_moving = TRUE;
8189
8190       Feld[x][y] =
8191         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8192          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8193          EL_DC_MAGIC_WALL_FILLING);
8194       Store[x][y] = element;
8195     }
8196     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8197     {
8198       SplashAcid(x, y + 1);
8199
8200       InitMovingField(x, y, MV_DOWN);
8201       started_moving = TRUE;
8202
8203       Store[x][y] = EL_ACID;
8204     }
8205     else if (
8206 #if USE_FIX_IMPACT_COLLISION
8207              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8208               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8209 #else
8210              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8211               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8212 #endif
8213              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8214               CAN_FALL(element) && WasJustFalling[x][y] &&
8215               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8216
8217              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8218               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8219               (Feld[x][y + 1] == EL_BLOCKED)))
8220     {
8221       /* this is needed for a special case not covered by calling "Impact()"
8222          from "ContinueMoving()": if an element moves to a tile directly below
8223          another element which was just falling on that tile (which was empty
8224          in the previous frame), the falling element above would just stop
8225          instead of smashing the element below (in previous version, the above
8226          element was just checked for "moving" instead of "falling", resulting
8227          in incorrect smashes caused by horizontal movement of the above
8228          element; also, the case of the player being the element to smash was
8229          simply not covered here... :-/ ) */
8230
8231       CheckCollision[x][y] = 0;
8232       CheckImpact[x][y] = 0;
8233
8234       Impact(x, y);
8235     }
8236     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8237     {
8238       if (MovDir[x][y] == MV_NONE)
8239       {
8240         InitMovingField(x, y, MV_DOWN);
8241         started_moving = TRUE;
8242       }
8243     }
8244     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8245     {
8246       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8247         MovDir[x][y] = MV_DOWN;
8248
8249       InitMovingField(x, y, MV_DOWN);
8250       started_moving = TRUE;
8251     }
8252     else if (element == EL_AMOEBA_DROP)
8253     {
8254       Feld[x][y] = EL_AMOEBA_GROWING;
8255       Store[x][y] = EL_AMOEBA_WET;
8256     }
8257     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8258               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8259              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8260              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8261     {
8262       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8263                                 (IS_FREE(x - 1, y + 1) ||
8264                                  Feld[x - 1][y + 1] == EL_ACID));
8265       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8266                                 (IS_FREE(x + 1, y + 1) ||
8267                                  Feld[x + 1][y + 1] == EL_ACID));
8268       boolean can_fall_any  = (can_fall_left || can_fall_right);
8269       boolean can_fall_both = (can_fall_left && can_fall_right);
8270       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8271
8272 #if USE_NEW_ALL_SLIPPERY
8273       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8274       {
8275         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8276           can_fall_right = FALSE;
8277         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8278           can_fall_left = FALSE;
8279         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8280           can_fall_right = FALSE;
8281         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8282           can_fall_left = FALSE;
8283
8284         can_fall_any  = (can_fall_left || can_fall_right);
8285         can_fall_both = FALSE;
8286       }
8287 #else
8288       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8289       {
8290         if (slippery_type == SLIPPERY_ONLY_LEFT)
8291           can_fall_right = FALSE;
8292         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8293           can_fall_left = FALSE;
8294         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8295           can_fall_right = FALSE;
8296         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8297           can_fall_left = FALSE;
8298
8299         can_fall_any  = (can_fall_left || can_fall_right);
8300         can_fall_both = (can_fall_left && can_fall_right);
8301       }
8302 #endif
8303
8304 #if USE_NEW_ALL_SLIPPERY
8305 #else
8306 #if USE_NEW_SP_SLIPPERY
8307       /* !!! better use the same properties as for custom elements here !!! */
8308       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8309                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8310       {
8311         can_fall_right = FALSE;         /* slip down on left side */
8312         can_fall_both = FALSE;
8313       }
8314 #endif
8315 #endif
8316
8317 #if USE_NEW_ALL_SLIPPERY
8318       if (can_fall_both)
8319       {
8320         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8321           can_fall_right = FALSE;       /* slip down on left side */
8322         else
8323           can_fall_left = !(can_fall_right = RND(2));
8324
8325         can_fall_both = FALSE;
8326       }
8327 #else
8328       if (can_fall_both)
8329       {
8330         if (game.emulation == EMU_BOULDERDASH ||
8331             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8332           can_fall_right = FALSE;       /* slip down on left side */
8333         else
8334           can_fall_left = !(can_fall_right = RND(2));
8335
8336         can_fall_both = FALSE;
8337       }
8338 #endif
8339
8340       if (can_fall_any)
8341       {
8342         /* if not determined otherwise, prefer left side for slipping down */
8343         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8344         started_moving = TRUE;
8345       }
8346     }
8347 #if 0
8348     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8349 #else
8350     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8351 #endif
8352     {
8353       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8354       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8355       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8356       int belt_dir = game.belt_dir[belt_nr];
8357
8358       if ((belt_dir == MV_LEFT  && left_is_free) ||
8359           (belt_dir == MV_RIGHT && right_is_free))
8360       {
8361         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8362
8363         InitMovingField(x, y, belt_dir);
8364         started_moving = TRUE;
8365
8366         Pushed[x][y] = TRUE;
8367         Pushed[nextx][y] = TRUE;
8368
8369         GfxAction[x][y] = ACTION_DEFAULT;
8370       }
8371       else
8372       {
8373         MovDir[x][y] = 0;       /* if element was moving, stop it */
8374       }
8375     }
8376   }
8377
8378   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8379 #if 0
8380   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8381 #else
8382   if (CAN_MOVE(element) && !started_moving)
8383 #endif
8384   {
8385     int move_pattern = element_info[element].move_pattern;
8386     int newx, newy;
8387
8388 #if 0
8389 #if DEBUG
8390     if (MovDir[x][y] == MV_NONE)
8391     {
8392       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8393              x, y, element, element_info[element].token_name);
8394       printf("StartMoving(): This should never happen!\n");
8395     }
8396 #endif
8397 #endif
8398
8399     Moving2Blocked(x, y, &newx, &newy);
8400
8401     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8402       return;
8403
8404     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8405         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8406     {
8407       WasJustMoving[x][y] = 0;
8408       CheckCollision[x][y] = 0;
8409
8410       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8411
8412       if (Feld[x][y] != element)        /* element has changed */
8413         return;
8414     }
8415
8416     if (!MovDelay[x][y])        /* start new movement phase */
8417     {
8418       /* all objects that can change their move direction after each step
8419          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8420
8421       if (element != EL_YAMYAM &&
8422           element != EL_DARK_YAMYAM &&
8423           element != EL_PACMAN &&
8424           !(move_pattern & MV_ANY_DIRECTION) &&
8425           move_pattern != MV_TURNING_LEFT &&
8426           move_pattern != MV_TURNING_RIGHT &&
8427           move_pattern != MV_TURNING_LEFT_RIGHT &&
8428           move_pattern != MV_TURNING_RIGHT_LEFT &&
8429           move_pattern != MV_TURNING_RANDOM)
8430       {
8431         TurnRound(x, y);
8432
8433         if (MovDelay[x][y] && (element == EL_BUG ||
8434                                element == EL_SPACESHIP ||
8435                                element == EL_SP_SNIKSNAK ||
8436                                element == EL_SP_ELECTRON ||
8437                                element == EL_MOLE))
8438           TEST_DrawLevelField(x, y);
8439       }
8440     }
8441
8442     if (MovDelay[x][y])         /* wait some time before next movement */
8443     {
8444       MovDelay[x][y]--;
8445
8446       if (element == EL_ROBOT ||
8447           element == EL_YAMYAM ||
8448           element == EL_DARK_YAMYAM)
8449       {
8450         DrawLevelElementAnimationIfNeeded(x, y, element);
8451         PlayLevelSoundAction(x, y, ACTION_WAITING);
8452       }
8453       else if (element == EL_SP_ELECTRON)
8454         DrawLevelElementAnimationIfNeeded(x, y, element);
8455       else if (element == EL_DRAGON)
8456       {
8457         int i;
8458         int dir = MovDir[x][y];
8459         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8460         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8461         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8462                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8463                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8464                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8465         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8466
8467         GfxAction[x][y] = ACTION_ATTACKING;
8468
8469         if (IS_PLAYER(x, y))
8470           DrawPlayerField(x, y);
8471         else
8472           TEST_DrawLevelField(x, y);
8473
8474         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8475
8476         for (i = 1; i <= 3; i++)
8477         {
8478           int xx = x + i * dx;
8479           int yy = y + i * dy;
8480           int sx = SCREENX(xx);
8481           int sy = SCREENY(yy);
8482           int flame_graphic = graphic + (i - 1);
8483
8484           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8485             break;
8486
8487           if (MovDelay[x][y])
8488           {
8489             int flamed = MovingOrBlocked2Element(xx, yy);
8490
8491             /* !!! */
8492 #if 0
8493             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8494               Bang(xx, yy);
8495             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8496               RemoveMovingField(xx, yy);
8497             else
8498               RemoveField(xx, yy);
8499 #else
8500             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8501               Bang(xx, yy);
8502             else
8503               RemoveMovingField(xx, yy);
8504 #endif
8505
8506             ChangeDelay[xx][yy] = 0;
8507
8508             Feld[xx][yy] = EL_FLAMES;
8509
8510             if (IN_SCR_FIELD(sx, sy))
8511             {
8512               TEST_DrawLevelFieldCrumbled(xx, yy);
8513               DrawGraphic(sx, sy, flame_graphic, frame);
8514             }
8515           }
8516           else
8517           {
8518             if (Feld[xx][yy] == EL_FLAMES)
8519               Feld[xx][yy] = EL_EMPTY;
8520             TEST_DrawLevelField(xx, yy);
8521           }
8522         }
8523       }
8524
8525       if (MovDelay[x][y])       /* element still has to wait some time */
8526       {
8527         PlayLevelSoundAction(x, y, ACTION_WAITING);
8528
8529         return;
8530       }
8531     }
8532
8533     /* now make next step */
8534
8535     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8536
8537     if (DONT_COLLIDE_WITH(element) &&
8538         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8539         !PLAYER_ENEMY_PROTECTED(newx, newy))
8540     {
8541       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8542
8543       return;
8544     }
8545
8546     else if (CAN_MOVE_INTO_ACID(element) &&
8547              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8548              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8549              (MovDir[x][y] == MV_DOWN ||
8550               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8551     {
8552       SplashAcid(newx, newy);
8553       Store[x][y] = EL_ACID;
8554     }
8555     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8556     {
8557       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8558           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8559           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8560           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8561       {
8562         RemoveField(x, y);
8563         TEST_DrawLevelField(x, y);
8564
8565         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8566         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8567           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8568
8569         local_player->friends_still_needed--;
8570         if (!local_player->friends_still_needed &&
8571             !local_player->GameOver && AllPlayersGone)
8572           PlayerWins(local_player);
8573
8574         return;
8575       }
8576       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8577       {
8578         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8579           TEST_DrawLevelField(newx, newy);
8580         else
8581           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8582       }
8583       else if (!IS_FREE(newx, newy))
8584       {
8585         GfxAction[x][y] = ACTION_WAITING;
8586
8587         if (IS_PLAYER(x, y))
8588           DrawPlayerField(x, y);
8589         else
8590           TEST_DrawLevelField(x, y);
8591
8592         return;
8593       }
8594     }
8595     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8596     {
8597       if (IS_FOOD_PIG(Feld[newx][newy]))
8598       {
8599         if (IS_MOVING(newx, newy))
8600           RemoveMovingField(newx, newy);
8601         else
8602         {
8603           Feld[newx][newy] = EL_EMPTY;
8604           TEST_DrawLevelField(newx, newy);
8605         }
8606
8607         PlayLevelSound(x, y, SND_PIG_DIGGING);
8608       }
8609       else if (!IS_FREE(newx, newy))
8610       {
8611         if (IS_PLAYER(x, y))
8612           DrawPlayerField(x, y);
8613         else
8614           TEST_DrawLevelField(x, y);
8615
8616         return;
8617       }
8618     }
8619     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8620     {
8621       if (Store[x][y] != EL_EMPTY)
8622       {
8623         boolean can_clone = FALSE;
8624         int xx, yy;
8625
8626         /* check if element to clone is still there */
8627         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8628         {
8629           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8630           {
8631             can_clone = TRUE;
8632
8633             break;
8634           }
8635         }
8636
8637         /* cannot clone or target field not free anymore -- do not clone */
8638         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8639           Store[x][y] = EL_EMPTY;
8640       }
8641
8642       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8643       {
8644         if (IS_MV_DIAGONAL(MovDir[x][y]))
8645         {
8646           int diagonal_move_dir = MovDir[x][y];
8647           int stored = Store[x][y];
8648           int change_delay = 8;
8649           int graphic;
8650
8651           /* android is moving diagonally */
8652
8653           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8654
8655           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8656           GfxElement[x][y] = EL_EMC_ANDROID;
8657           GfxAction[x][y] = ACTION_SHRINKING;
8658           GfxDir[x][y] = diagonal_move_dir;
8659           ChangeDelay[x][y] = change_delay;
8660
8661           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8662                                    GfxDir[x][y]);
8663
8664           DrawLevelGraphicAnimation(x, y, graphic);
8665           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8666
8667           if (Feld[newx][newy] == EL_ACID)
8668           {
8669             SplashAcid(newx, newy);
8670
8671             return;
8672           }
8673
8674           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8675
8676           Store[newx][newy] = EL_EMC_ANDROID;
8677           GfxElement[newx][newy] = EL_EMC_ANDROID;
8678           GfxAction[newx][newy] = ACTION_GROWING;
8679           GfxDir[newx][newy] = diagonal_move_dir;
8680           ChangeDelay[newx][newy] = change_delay;
8681
8682           graphic = el_act_dir2img(GfxElement[newx][newy],
8683                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8684
8685           DrawLevelGraphicAnimation(newx, newy, graphic);
8686           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8687
8688           return;
8689         }
8690         else
8691         {
8692           Feld[newx][newy] = EL_EMPTY;
8693           TEST_DrawLevelField(newx, newy);
8694
8695           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8696         }
8697       }
8698       else if (!IS_FREE(newx, newy))
8699       {
8700 #if 0
8701         if (IS_PLAYER(x, y))
8702           DrawPlayerField(x, y);
8703         else
8704           TEST_DrawLevelField(x, y);
8705 #endif
8706
8707         return;
8708       }
8709     }
8710     else if (IS_CUSTOM_ELEMENT(element) &&
8711              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8712     {
8713 #if 1
8714       if (!DigFieldByCE(newx, newy, element))
8715         return;
8716 #else
8717       int new_element = Feld[newx][newy];
8718
8719       if (!IS_FREE(newx, newy))
8720       {
8721         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8722                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8723                       ACTION_BREAKING);
8724
8725         /* no element can dig solid indestructible elements */
8726         if (IS_INDESTRUCTIBLE(new_element) &&
8727             !IS_DIGGABLE(new_element) &&
8728             !IS_COLLECTIBLE(new_element))
8729           return;
8730
8731         if (AmoebaNr[newx][newy] &&
8732             (new_element == EL_AMOEBA_FULL ||
8733              new_element == EL_BD_AMOEBA ||
8734              new_element == EL_AMOEBA_GROWING))
8735         {
8736           AmoebaCnt[AmoebaNr[newx][newy]]--;
8737           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8738         }
8739
8740         if (IS_MOVING(newx, newy))
8741           RemoveMovingField(newx, newy);
8742         else
8743         {
8744           RemoveField(newx, newy);
8745           TEST_DrawLevelField(newx, newy);
8746         }
8747
8748         /* if digged element was about to explode, prevent the explosion */
8749         ExplodeField[newx][newy] = EX_TYPE_NONE;
8750
8751         PlayLevelSoundAction(x, y, action);
8752       }
8753
8754       Store[newx][newy] = EL_EMPTY;
8755
8756 #if 1
8757       /* this makes it possible to leave the removed element again */
8758       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8759         Store[newx][newy] = new_element;
8760 #else
8761       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8762       {
8763         int move_leave_element = element_info[element].move_leave_element;
8764
8765         /* this makes it possible to leave the removed element again */
8766         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8767                              new_element : move_leave_element);
8768       }
8769 #endif
8770
8771 #endif
8772
8773       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8774       {
8775         RunnerVisit[x][y] = FrameCounter;
8776         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8777       }
8778     }
8779     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8780     {
8781       if (!IS_FREE(newx, newy))
8782       {
8783         if (IS_PLAYER(x, y))
8784           DrawPlayerField(x, y);
8785         else
8786           TEST_DrawLevelField(x, y);
8787
8788         return;
8789       }
8790       else
8791       {
8792         boolean wanna_flame = !RND(10);
8793         int dx = newx - x, dy = newy - y;
8794         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8795         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8796         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8797                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8798         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8799                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8800
8801         if ((wanna_flame ||
8802              IS_CLASSIC_ENEMY(element1) ||
8803              IS_CLASSIC_ENEMY(element2)) &&
8804             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8805             element1 != EL_FLAMES && element2 != EL_FLAMES)
8806         {
8807           ResetGfxAnimation(x, y);
8808           GfxAction[x][y] = ACTION_ATTACKING;
8809
8810           if (IS_PLAYER(x, y))
8811             DrawPlayerField(x, y);
8812           else
8813             TEST_DrawLevelField(x, y);
8814
8815           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8816
8817           MovDelay[x][y] = 50;
8818
8819           /* !!! */
8820 #if 0
8821           RemoveField(newx, newy);
8822 #endif
8823           Feld[newx][newy] = EL_FLAMES;
8824           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8825           {
8826 #if 0
8827             RemoveField(newx1, newy1);
8828 #endif
8829             Feld[newx1][newy1] = EL_FLAMES;
8830           }
8831           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8832           {
8833 #if 0
8834             RemoveField(newx2, newy2);
8835 #endif
8836             Feld[newx2][newy2] = EL_FLAMES;
8837           }
8838
8839           return;
8840         }
8841       }
8842     }
8843     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8844              Feld[newx][newy] == EL_DIAMOND)
8845     {
8846       if (IS_MOVING(newx, newy))
8847         RemoveMovingField(newx, newy);
8848       else
8849       {
8850         Feld[newx][newy] = EL_EMPTY;
8851         TEST_DrawLevelField(newx, newy);
8852       }
8853
8854       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8855     }
8856     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8857              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8858     {
8859       if (AmoebaNr[newx][newy])
8860       {
8861         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8862         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8863             Feld[newx][newy] == EL_BD_AMOEBA)
8864           AmoebaCnt[AmoebaNr[newx][newy]]--;
8865       }
8866
8867 #if 0
8868       /* !!! test !!! */
8869       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8870       {
8871         RemoveMovingField(newx, newy);
8872       }
8873 #else
8874       if (IS_MOVING(newx, newy))
8875       {
8876         RemoveMovingField(newx, newy);
8877       }
8878 #endif
8879       else
8880       {
8881         Feld[newx][newy] = EL_EMPTY;
8882         TEST_DrawLevelField(newx, newy);
8883       }
8884
8885       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8886     }
8887     else if ((element == EL_PACMAN || element == EL_MOLE)
8888              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8889     {
8890       if (AmoebaNr[newx][newy])
8891       {
8892         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8893         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8894             Feld[newx][newy] == EL_BD_AMOEBA)
8895           AmoebaCnt[AmoebaNr[newx][newy]]--;
8896       }
8897
8898       if (element == EL_MOLE)
8899       {
8900         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8901         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8902
8903         ResetGfxAnimation(x, y);
8904         GfxAction[x][y] = ACTION_DIGGING;
8905         TEST_DrawLevelField(x, y);
8906
8907         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8908
8909         return;                         /* wait for shrinking amoeba */
8910       }
8911       else      /* element == EL_PACMAN */
8912       {
8913         Feld[newx][newy] = EL_EMPTY;
8914         TEST_DrawLevelField(newx, newy);
8915         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8916       }
8917     }
8918     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8919              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8920               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8921     {
8922       /* wait for shrinking amoeba to completely disappear */
8923       return;
8924     }
8925     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8926     {
8927       /* object was running against a wall */
8928
8929       TurnRound(x, y);
8930
8931 #if 0
8932       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8933       if (move_pattern & MV_ANY_DIRECTION &&
8934           move_pattern == MovDir[x][y])
8935       {
8936         int blocking_element =
8937           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8938
8939         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8940                                  MovDir[x][y]);
8941
8942         element = Feld[x][y];   /* element might have changed */
8943       }
8944 #endif
8945
8946       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8947         DrawLevelElementAnimation(x, y, element);
8948
8949       if (DONT_TOUCH(element))
8950         TestIfBadThingTouchesPlayer(x, y);
8951
8952       return;
8953     }
8954
8955     InitMovingField(x, y, MovDir[x][y]);
8956
8957     PlayLevelSoundAction(x, y, ACTION_MOVING);
8958   }
8959
8960   if (MovDir[x][y])
8961     ContinueMoving(x, y);
8962 }
8963
8964 void ContinueMoving(int x, int y)
8965 {
8966   int element = Feld[x][y];
8967   struct ElementInfo *ei = &element_info[element];
8968   int direction = MovDir[x][y];
8969   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8970   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8971   int newx = x + dx, newy = y + dy;
8972   int stored = Store[x][y];
8973   int stored_new = Store[newx][newy];
8974   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8975   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8976   boolean last_line = (newy == lev_fieldy - 1);
8977
8978   MovPos[x][y] += getElementMoveStepsize(x, y);
8979
8980   if (pushed_by_player) /* special case: moving object pushed by player */
8981     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8982
8983   if (ABS(MovPos[x][y]) < TILEX)
8984   {
8985 #if 0
8986     int ee = Feld[x][y];
8987     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8988     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8989
8990     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8991            x, y, ABS(MovPos[x][y]),
8992            ee, gg, ff,
8993            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8994 #endif
8995
8996     TEST_DrawLevelField(x, y);
8997
8998     return;     /* element is still moving */
8999   }
9000
9001   /* element reached destination field */
9002
9003   Feld[x][y] = EL_EMPTY;
9004   Feld[newx][newy] = element;
9005   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9006
9007   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9008   {
9009     element = Feld[newx][newy] = EL_ACID;
9010   }
9011   else if (element == EL_MOLE)
9012   {
9013     Feld[x][y] = EL_SAND;
9014
9015     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9016   }
9017   else if (element == EL_QUICKSAND_FILLING)
9018   {
9019     element = Feld[newx][newy] = get_next_element(element);
9020     Store[newx][newy] = Store[x][y];
9021   }
9022   else if (element == EL_QUICKSAND_EMPTYING)
9023   {
9024     Feld[x][y] = get_next_element(element);
9025     element = Feld[newx][newy] = Store[x][y];
9026   }
9027   else if (element == EL_QUICKSAND_FAST_FILLING)
9028   {
9029     element = Feld[newx][newy] = get_next_element(element);
9030     Store[newx][newy] = Store[x][y];
9031   }
9032   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9033   {
9034     Feld[x][y] = get_next_element(element);
9035     element = Feld[newx][newy] = Store[x][y];
9036   }
9037   else if (element == EL_MAGIC_WALL_FILLING)
9038   {
9039     element = Feld[newx][newy] = get_next_element(element);
9040     if (!game.magic_wall_active)
9041       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9042     Store[newx][newy] = Store[x][y];
9043   }
9044   else if (element == EL_MAGIC_WALL_EMPTYING)
9045   {
9046     Feld[x][y] = get_next_element(element);
9047     if (!game.magic_wall_active)
9048       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9049     element = Feld[newx][newy] = Store[x][y];
9050
9051 #if USE_NEW_CUSTOM_VALUE
9052     InitField(newx, newy, FALSE);
9053 #endif
9054   }
9055   else if (element == EL_BD_MAGIC_WALL_FILLING)
9056   {
9057     element = Feld[newx][newy] = get_next_element(element);
9058     if (!game.magic_wall_active)
9059       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9060     Store[newx][newy] = Store[x][y];
9061   }
9062   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9063   {
9064     Feld[x][y] = get_next_element(element);
9065     if (!game.magic_wall_active)
9066       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9067     element = Feld[newx][newy] = Store[x][y];
9068
9069 #if USE_NEW_CUSTOM_VALUE
9070     InitField(newx, newy, FALSE);
9071 #endif
9072   }
9073   else if (element == EL_DC_MAGIC_WALL_FILLING)
9074   {
9075     element = Feld[newx][newy] = get_next_element(element);
9076     if (!game.magic_wall_active)
9077       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9078     Store[newx][newy] = Store[x][y];
9079   }
9080   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9081   {
9082     Feld[x][y] = get_next_element(element);
9083     if (!game.magic_wall_active)
9084       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9085     element = Feld[newx][newy] = Store[x][y];
9086
9087 #if USE_NEW_CUSTOM_VALUE
9088     InitField(newx, newy, FALSE);
9089 #endif
9090   }
9091   else if (element == EL_AMOEBA_DROPPING)
9092   {
9093     Feld[x][y] = get_next_element(element);
9094     element = Feld[newx][newy] = Store[x][y];
9095   }
9096   else if (element == EL_SOKOBAN_OBJECT)
9097   {
9098     if (Back[x][y])
9099       Feld[x][y] = Back[x][y];
9100
9101     if (Back[newx][newy])
9102       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9103
9104     Back[x][y] = Back[newx][newy] = 0;
9105   }
9106
9107   Store[x][y] = EL_EMPTY;
9108   MovPos[x][y] = 0;
9109   MovDir[x][y] = 0;
9110   MovDelay[x][y] = 0;
9111
9112   MovDelay[newx][newy] = 0;
9113
9114   if (CAN_CHANGE_OR_HAS_ACTION(element))
9115   {
9116     /* copy element change control values to new field */
9117     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9118     ChangePage[newx][newy]  = ChangePage[x][y];
9119     ChangeCount[newx][newy] = ChangeCount[x][y];
9120     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9121   }
9122
9123 #if USE_NEW_CUSTOM_VALUE
9124   CustomValue[newx][newy] = CustomValue[x][y];
9125 #endif
9126
9127   ChangeDelay[x][y] = 0;
9128   ChangePage[x][y] = -1;
9129   ChangeCount[x][y] = 0;
9130   ChangeEvent[x][y] = -1;
9131
9132 #if USE_NEW_CUSTOM_VALUE
9133   CustomValue[x][y] = 0;
9134 #endif
9135
9136   /* copy animation control values to new field */
9137   GfxFrame[newx][newy]  = GfxFrame[x][y];
9138   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9139   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9140   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9141
9142   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9143
9144   /* some elements can leave other elements behind after moving */
9145 #if 1
9146   if (ei->move_leave_element != EL_EMPTY &&
9147       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9148       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9149 #else
9150   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9151       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9152       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9153 #endif
9154   {
9155     int move_leave_element = ei->move_leave_element;
9156
9157 #if 1
9158 #if 1
9159     /* this makes it possible to leave the removed element again */
9160     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9161       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9162 #else
9163     /* this makes it possible to leave the removed element again */
9164     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9165       move_leave_element = stored;
9166 #endif
9167 #else
9168     /* this makes it possible to leave the removed element again */
9169     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9170         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9171       move_leave_element = stored;
9172 #endif
9173
9174     Feld[x][y] = move_leave_element;
9175
9176     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9177       MovDir[x][y] = direction;
9178
9179     InitField(x, y, FALSE);
9180
9181     if (GFX_CRUMBLED(Feld[x][y]))
9182       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9183
9184     if (ELEM_IS_PLAYER(move_leave_element))
9185       RelocatePlayer(x, y, move_leave_element);
9186   }
9187
9188   /* do this after checking for left-behind element */
9189   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9190
9191   if (!CAN_MOVE(element) ||
9192       (CAN_FALL(element) && direction == MV_DOWN &&
9193        (element == EL_SPRING ||
9194         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9195         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9196     GfxDir[x][y] = MovDir[newx][newy] = 0;
9197
9198   TEST_DrawLevelField(x, y);
9199   TEST_DrawLevelField(newx, newy);
9200
9201   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9202
9203   /* prevent pushed element from moving on in pushed direction */
9204   if (pushed_by_player && CAN_MOVE(element) &&
9205       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9206       !(element_info[element].move_pattern & direction))
9207     TurnRound(newx, newy);
9208
9209   /* prevent elements on conveyor belt from moving on in last direction */
9210   if (pushed_by_conveyor && CAN_FALL(element) &&
9211       direction & MV_HORIZONTAL)
9212     MovDir[newx][newy] = 0;
9213
9214   if (!pushed_by_player)
9215   {
9216     int nextx = newx + dx, nexty = newy + dy;
9217     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9218
9219     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9220
9221     if (CAN_FALL(element) && direction == MV_DOWN)
9222       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9223
9224     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9225       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9226
9227 #if USE_FIX_IMPACT_COLLISION
9228     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9229       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9230 #endif
9231   }
9232
9233   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9234   {
9235     TestIfBadThingTouchesPlayer(newx, newy);
9236     TestIfBadThingTouchesFriend(newx, newy);
9237
9238     if (!IS_CUSTOM_ELEMENT(element))
9239       TestIfBadThingTouchesOtherBadThing(newx, newy);
9240   }
9241   else if (element == EL_PENGUIN)
9242     TestIfFriendTouchesBadThing(newx, newy);
9243
9244   if (DONT_GET_HIT_BY(element))
9245   {
9246     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9247   }
9248
9249   /* give the player one last chance (one more frame) to move away */
9250   if (CAN_FALL(element) && direction == MV_DOWN &&
9251       (last_line || (!IS_FREE(x, newy + 1) &&
9252                      (!IS_PLAYER(x, newy + 1) ||
9253                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9254     Impact(x, newy);
9255
9256   if (pushed_by_player && !game.use_change_when_pushing_bug)
9257   {
9258     int push_side = MV_DIR_OPPOSITE(direction);
9259     struct PlayerInfo *player = PLAYERINFO(x, y);
9260
9261     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9262                                player->index_bit, push_side);
9263     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9264                                         player->index_bit, push_side);
9265   }
9266
9267   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9268     MovDelay[newx][newy] = 1;
9269
9270   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9271
9272   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9273
9274 #if 0
9275   if (ChangePage[newx][newy] != -1)             /* delayed change */
9276   {
9277     int page = ChangePage[newx][newy];
9278     struct ElementChangeInfo *change = &ei->change_page[page];
9279
9280     ChangePage[newx][newy] = -1;
9281
9282     if (change->can_change)
9283     {
9284       if (ChangeElement(newx, newy, element, page))
9285       {
9286         if (change->post_change_function)
9287           change->post_change_function(newx, newy);
9288       }
9289     }
9290
9291     if (change->has_action)
9292       ExecuteCustomElementAction(newx, newy, element, page);
9293   }
9294 #endif
9295
9296   TestIfElementHitsCustomElement(newx, newy, direction);
9297   TestIfPlayerTouchesCustomElement(newx, newy);
9298   TestIfElementTouchesCustomElement(newx, newy);
9299
9300   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9301       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9302     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9303                              MV_DIR_OPPOSITE(direction));
9304 }
9305
9306 int AmoebeNachbarNr(int ax, int ay)
9307 {
9308   int i;
9309   int element = Feld[ax][ay];
9310   int group_nr = 0;
9311   static int xy[4][2] =
9312   {
9313     { 0, -1 },
9314     { -1, 0 },
9315     { +1, 0 },
9316     { 0, +1 }
9317   };
9318
9319   for (i = 0; i < NUM_DIRECTIONS; i++)
9320   {
9321     int x = ax + xy[i][0];
9322     int y = ay + xy[i][1];
9323
9324     if (!IN_LEV_FIELD(x, y))
9325       continue;
9326
9327     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9328       group_nr = AmoebaNr[x][y];
9329   }
9330
9331   return group_nr;
9332 }
9333
9334 void AmoebenVereinigen(int ax, int ay)
9335 {
9336   int i, x, y, xx, yy;
9337   int new_group_nr = AmoebaNr[ax][ay];
9338   static int xy[4][2] =
9339   {
9340     { 0, -1 },
9341     { -1, 0 },
9342     { +1, 0 },
9343     { 0, +1 }
9344   };
9345
9346   if (new_group_nr == 0)
9347     return;
9348
9349   for (i = 0; i < NUM_DIRECTIONS; i++)
9350   {
9351     x = ax + xy[i][0];
9352     y = ay + xy[i][1];
9353
9354     if (!IN_LEV_FIELD(x, y))
9355       continue;
9356
9357     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9358          Feld[x][y] == EL_BD_AMOEBA ||
9359          Feld[x][y] == EL_AMOEBA_DEAD) &&
9360         AmoebaNr[x][y] != new_group_nr)
9361     {
9362       int old_group_nr = AmoebaNr[x][y];
9363
9364       if (old_group_nr == 0)
9365         return;
9366
9367       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9368       AmoebaCnt[old_group_nr] = 0;
9369       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9370       AmoebaCnt2[old_group_nr] = 0;
9371
9372       SCAN_PLAYFIELD(xx, yy)
9373       {
9374         if (AmoebaNr[xx][yy] == old_group_nr)
9375           AmoebaNr[xx][yy] = new_group_nr;
9376       }
9377     }
9378   }
9379 }
9380
9381 void AmoebeUmwandeln(int ax, int ay)
9382 {
9383   int i, x, y;
9384
9385   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9386   {
9387     int group_nr = AmoebaNr[ax][ay];
9388
9389 #ifdef DEBUG
9390     if (group_nr == 0)
9391     {
9392       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9393       printf("AmoebeUmwandeln(): This should never happen!\n");
9394       return;
9395     }
9396 #endif
9397
9398     SCAN_PLAYFIELD(x, y)
9399     {
9400       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9401       {
9402         AmoebaNr[x][y] = 0;
9403         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9404       }
9405     }
9406
9407     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9408                             SND_AMOEBA_TURNING_TO_GEM :
9409                             SND_AMOEBA_TURNING_TO_ROCK));
9410     Bang(ax, ay);
9411   }
9412   else
9413   {
9414     static int xy[4][2] =
9415     {
9416       { 0, -1 },
9417       { -1, 0 },
9418       { +1, 0 },
9419       { 0, +1 }
9420     };
9421
9422     for (i = 0; i < NUM_DIRECTIONS; i++)
9423     {
9424       x = ax + xy[i][0];
9425       y = ay + xy[i][1];
9426
9427       if (!IN_LEV_FIELD(x, y))
9428         continue;
9429
9430       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9431       {
9432         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9433                               SND_AMOEBA_TURNING_TO_GEM :
9434                               SND_AMOEBA_TURNING_TO_ROCK));
9435         Bang(x, y);
9436       }
9437     }
9438   }
9439 }
9440
9441 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9442 {
9443   int x, y;
9444   int group_nr = AmoebaNr[ax][ay];
9445   boolean done = FALSE;
9446
9447 #ifdef DEBUG
9448   if (group_nr == 0)
9449   {
9450     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9451     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9452     return;
9453   }
9454 #endif
9455
9456   SCAN_PLAYFIELD(x, y)
9457   {
9458     if (AmoebaNr[x][y] == group_nr &&
9459         (Feld[x][y] == EL_AMOEBA_DEAD ||
9460          Feld[x][y] == EL_BD_AMOEBA ||
9461          Feld[x][y] == EL_AMOEBA_GROWING))
9462     {
9463       AmoebaNr[x][y] = 0;
9464       Feld[x][y] = new_element;
9465       InitField(x, y, FALSE);
9466       TEST_DrawLevelField(x, y);
9467       done = TRUE;
9468     }
9469   }
9470
9471   if (done)
9472     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9473                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9474                             SND_BD_AMOEBA_TURNING_TO_GEM));
9475 }
9476
9477 void AmoebeWaechst(int x, int y)
9478 {
9479   static unsigned long sound_delay = 0;
9480   static unsigned long sound_delay_value = 0;
9481
9482   if (!MovDelay[x][y])          /* start new growing cycle */
9483   {
9484     MovDelay[x][y] = 7;
9485
9486     if (DelayReached(&sound_delay, sound_delay_value))
9487     {
9488       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9489       sound_delay_value = 30;
9490     }
9491   }
9492
9493   if (MovDelay[x][y])           /* wait some time before growing bigger */
9494   {
9495     MovDelay[x][y]--;
9496     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9497     {
9498       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9499                                            6 - MovDelay[x][y]);
9500
9501       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9502     }
9503
9504     if (!MovDelay[x][y])
9505     {
9506       Feld[x][y] = Store[x][y];
9507       Store[x][y] = 0;
9508       TEST_DrawLevelField(x, y);
9509     }
9510   }
9511 }
9512
9513 void AmoebaDisappearing(int x, int y)
9514 {
9515   static unsigned long sound_delay = 0;
9516   static unsigned long sound_delay_value = 0;
9517
9518   if (!MovDelay[x][y])          /* start new shrinking cycle */
9519   {
9520     MovDelay[x][y] = 7;
9521
9522     if (DelayReached(&sound_delay, sound_delay_value))
9523       sound_delay_value = 30;
9524   }
9525
9526   if (MovDelay[x][y])           /* wait some time before shrinking */
9527   {
9528     MovDelay[x][y]--;
9529     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9530     {
9531       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9532                                            6 - MovDelay[x][y]);
9533
9534       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9535     }
9536
9537     if (!MovDelay[x][y])
9538     {
9539       Feld[x][y] = EL_EMPTY;
9540       TEST_DrawLevelField(x, y);
9541
9542       /* don't let mole enter this field in this cycle;
9543          (give priority to objects falling to this field from above) */
9544       Stop[x][y] = TRUE;
9545     }
9546   }
9547 }
9548
9549 void AmoebeAbleger(int ax, int ay)
9550 {
9551   int i;
9552   int element = Feld[ax][ay];
9553   int graphic = el2img(element);
9554   int newax = ax, neway = ay;
9555   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9556   static int xy[4][2] =
9557   {
9558     { 0, -1 },
9559     { -1, 0 },
9560     { +1, 0 },
9561     { 0, +1 }
9562   };
9563
9564   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9565   {
9566     Feld[ax][ay] = EL_AMOEBA_DEAD;
9567     TEST_DrawLevelField(ax, ay);
9568     return;
9569   }
9570
9571   if (IS_ANIMATED(graphic))
9572     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9573
9574   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9575     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9576
9577   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9578   {
9579     MovDelay[ax][ay]--;
9580     if (MovDelay[ax][ay])
9581       return;
9582   }
9583
9584   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9585   {
9586     int start = RND(4);
9587     int x = ax + xy[start][0];
9588     int y = ay + xy[start][1];
9589
9590     if (!IN_LEV_FIELD(x, y))
9591       return;
9592
9593     if (IS_FREE(x, y) ||
9594         CAN_GROW_INTO(Feld[x][y]) ||
9595         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9596         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9597     {
9598       newax = x;
9599       neway = y;
9600     }
9601
9602     if (newax == ax && neway == ay)
9603       return;
9604   }
9605   else                          /* normal or "filled" (BD style) amoeba */
9606   {
9607     int start = RND(4);
9608     boolean waiting_for_player = FALSE;
9609
9610     for (i = 0; i < NUM_DIRECTIONS; i++)
9611     {
9612       int j = (start + i) % 4;
9613       int x = ax + xy[j][0];
9614       int y = ay + xy[j][1];
9615
9616       if (!IN_LEV_FIELD(x, y))
9617         continue;
9618
9619       if (IS_FREE(x, y) ||
9620           CAN_GROW_INTO(Feld[x][y]) ||
9621           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9622           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9623       {
9624         newax = x;
9625         neway = y;
9626         break;
9627       }
9628       else if (IS_PLAYER(x, y))
9629         waiting_for_player = TRUE;
9630     }
9631
9632     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9633     {
9634       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9635       {
9636         Feld[ax][ay] = EL_AMOEBA_DEAD;
9637         TEST_DrawLevelField(ax, ay);
9638         AmoebaCnt[AmoebaNr[ax][ay]]--;
9639
9640         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9641         {
9642           if (element == EL_AMOEBA_FULL)
9643             AmoebeUmwandeln(ax, ay);
9644           else if (element == EL_BD_AMOEBA)
9645             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9646         }
9647       }
9648       return;
9649     }
9650     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9651     {
9652       /* amoeba gets larger by growing in some direction */
9653
9654       int new_group_nr = AmoebaNr[ax][ay];
9655
9656 #ifdef DEBUG
9657   if (new_group_nr == 0)
9658   {
9659     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9660     printf("AmoebeAbleger(): This should never happen!\n");
9661     return;
9662   }
9663 #endif
9664
9665       AmoebaNr[newax][neway] = new_group_nr;
9666       AmoebaCnt[new_group_nr]++;
9667       AmoebaCnt2[new_group_nr]++;
9668
9669       /* if amoeba touches other amoeba(s) after growing, unify them */
9670       AmoebenVereinigen(newax, neway);
9671
9672       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9673       {
9674         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9675         return;
9676       }
9677     }
9678   }
9679
9680   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9681       (neway == lev_fieldy - 1 && newax != ax))
9682   {
9683     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9684     Store[newax][neway] = element;
9685   }
9686   else if (neway == ay || element == EL_EMC_DRIPPER)
9687   {
9688     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9689
9690     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9691   }
9692   else
9693   {
9694     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9695     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9696     Store[ax][ay] = EL_AMOEBA_DROP;
9697     ContinueMoving(ax, ay);
9698     return;
9699   }
9700
9701   TEST_DrawLevelField(newax, neway);
9702 }
9703
9704 void Life(int ax, int ay)
9705 {
9706   int x1, y1, x2, y2;
9707   int life_time = 40;
9708   int element = Feld[ax][ay];
9709   int graphic = el2img(element);
9710   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9711                          level.biomaze);
9712   boolean changed = FALSE;
9713
9714   if (IS_ANIMATED(graphic))
9715     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9716
9717   if (Stop[ax][ay])
9718     return;
9719
9720   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9721     MovDelay[ax][ay] = life_time;
9722
9723   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9724   {
9725     MovDelay[ax][ay]--;
9726     if (MovDelay[ax][ay])
9727       return;
9728   }
9729
9730   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9731   {
9732     int xx = ax+x1, yy = ay+y1;
9733     int nachbarn = 0;
9734
9735     if (!IN_LEV_FIELD(xx, yy))
9736       continue;
9737
9738     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9739     {
9740       int x = xx+x2, y = yy+y2;
9741
9742       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9743         continue;
9744
9745       if (((Feld[x][y] == element ||
9746             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9747            !Stop[x][y]) ||
9748           (IS_FREE(x, y) && Stop[x][y]))
9749         nachbarn++;
9750     }
9751
9752     if (xx == ax && yy == ay)           /* field in the middle */
9753     {
9754       if (nachbarn < life_parameter[0] ||
9755           nachbarn > life_parameter[1])
9756       {
9757         Feld[xx][yy] = EL_EMPTY;
9758         if (!Stop[xx][yy])
9759           TEST_DrawLevelField(xx, yy);
9760         Stop[xx][yy] = TRUE;
9761         changed = TRUE;
9762       }
9763     }
9764     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9765     {                                   /* free border field */
9766       if (nachbarn >= life_parameter[2] &&
9767           nachbarn <= life_parameter[3])
9768       {
9769         Feld[xx][yy] = element;
9770         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9771         if (!Stop[xx][yy])
9772           TEST_DrawLevelField(xx, yy);
9773         Stop[xx][yy] = TRUE;
9774         changed = TRUE;
9775       }
9776     }
9777   }
9778
9779   if (changed)
9780     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9781                    SND_GAME_OF_LIFE_GROWING);
9782 }
9783
9784 static void InitRobotWheel(int x, int y)
9785 {
9786   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9787 }
9788
9789 static void RunRobotWheel(int x, int y)
9790 {
9791   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9792 }
9793
9794 static void StopRobotWheel(int x, int y)
9795 {
9796   if (ZX == x && ZY == y)
9797   {
9798     ZX = ZY = -1;
9799
9800     game.robot_wheel_active = FALSE;
9801   }
9802 }
9803
9804 static void InitTimegateWheel(int x, int y)
9805 {
9806   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9807 }
9808
9809 static void RunTimegateWheel(int x, int y)
9810 {
9811   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9812 }
9813
9814 static void InitMagicBallDelay(int x, int y)
9815 {
9816 #if 1
9817   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9818 #else
9819   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9820 #endif
9821 }
9822
9823 static void ActivateMagicBall(int bx, int by)
9824 {
9825   int x, y;
9826
9827   if (level.ball_random)
9828   {
9829     int pos_border = RND(8);    /* select one of the eight border elements */
9830     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9831     int xx = pos_content % 3;
9832     int yy = pos_content / 3;
9833
9834     x = bx - 1 + xx;
9835     y = by - 1 + yy;
9836
9837     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9838       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9839   }
9840   else
9841   {
9842     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9843     {
9844       int xx = x - bx + 1;
9845       int yy = y - by + 1;
9846
9847       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9848         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9849     }
9850   }
9851
9852   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9853 }
9854
9855 void CheckExit(int x, int y)
9856 {
9857   if (local_player->gems_still_needed > 0 ||
9858       local_player->sokobanfields_still_needed > 0 ||
9859       local_player->lights_still_needed > 0)
9860   {
9861     int element = Feld[x][y];
9862     int graphic = el2img(element);
9863
9864     if (IS_ANIMATED(graphic))
9865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9866
9867     return;
9868   }
9869
9870   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9871     return;
9872
9873   Feld[x][y] = EL_EXIT_OPENING;
9874
9875   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9876 }
9877
9878 void CheckExitEM(int x, int y)
9879 {
9880   if (local_player->gems_still_needed > 0 ||
9881       local_player->sokobanfields_still_needed > 0 ||
9882       local_player->lights_still_needed > 0)
9883   {
9884     int element = Feld[x][y];
9885     int graphic = el2img(element);
9886
9887     if (IS_ANIMATED(graphic))
9888       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9889
9890     return;
9891   }
9892
9893   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9894     return;
9895
9896   Feld[x][y] = EL_EM_EXIT_OPENING;
9897
9898   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9899 }
9900
9901 void CheckExitSteel(int x, int y)
9902 {
9903   if (local_player->gems_still_needed > 0 ||
9904       local_player->sokobanfields_still_needed > 0 ||
9905       local_player->lights_still_needed > 0)
9906   {
9907     int element = Feld[x][y];
9908     int graphic = el2img(element);
9909
9910     if (IS_ANIMATED(graphic))
9911       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9912
9913     return;
9914   }
9915
9916   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9917     return;
9918
9919   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9920
9921   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9922 }
9923
9924 void CheckExitSteelEM(int x, int y)
9925 {
9926   if (local_player->gems_still_needed > 0 ||
9927       local_player->sokobanfields_still_needed > 0 ||
9928       local_player->lights_still_needed > 0)
9929   {
9930     int element = Feld[x][y];
9931     int graphic = el2img(element);
9932
9933     if (IS_ANIMATED(graphic))
9934       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9935
9936     return;
9937   }
9938
9939   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9940     return;
9941
9942   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9943
9944   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9945 }
9946
9947 void CheckExitSP(int x, int y)
9948 {
9949   if (local_player->gems_still_needed > 0)
9950   {
9951     int element = Feld[x][y];
9952     int graphic = el2img(element);
9953
9954     if (IS_ANIMATED(graphic))
9955       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9956
9957     return;
9958   }
9959
9960   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9961     return;
9962
9963   Feld[x][y] = EL_SP_EXIT_OPENING;
9964
9965   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9966 }
9967
9968 static void CloseAllOpenTimegates()
9969 {
9970   int x, y;
9971
9972   SCAN_PLAYFIELD(x, y)
9973   {
9974     int element = Feld[x][y];
9975
9976     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9977     {
9978       Feld[x][y] = EL_TIMEGATE_CLOSING;
9979
9980       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9981     }
9982   }
9983 }
9984
9985 void DrawTwinkleOnField(int x, int y)
9986 {
9987   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9988     return;
9989
9990   if (Feld[x][y] == EL_BD_DIAMOND)
9991     return;
9992
9993   if (MovDelay[x][y] == 0)      /* next animation frame */
9994     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9995
9996   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9997   {
9998     MovDelay[x][y]--;
9999
10000     DrawLevelElementAnimation(x, y, Feld[x][y]);
10001
10002     if (MovDelay[x][y] != 0)
10003     {
10004       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10005                                            10 - MovDelay[x][y]);
10006
10007       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10008     }
10009   }
10010 }
10011
10012 void MauerWaechst(int x, int y)
10013 {
10014   int delay = 6;
10015
10016   if (!MovDelay[x][y])          /* next animation frame */
10017     MovDelay[x][y] = 3 * delay;
10018
10019   if (MovDelay[x][y])           /* wait some time before next frame */
10020   {
10021     MovDelay[x][y]--;
10022
10023     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10024     {
10025       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10026       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10027
10028       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10029     }
10030
10031     if (!MovDelay[x][y])
10032     {
10033       if (MovDir[x][y] == MV_LEFT)
10034       {
10035         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10036           TEST_DrawLevelField(x - 1, y);
10037       }
10038       else if (MovDir[x][y] == MV_RIGHT)
10039       {
10040         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10041           TEST_DrawLevelField(x + 1, y);
10042       }
10043       else if (MovDir[x][y] == MV_UP)
10044       {
10045         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10046           TEST_DrawLevelField(x, y - 1);
10047       }
10048       else
10049       {
10050         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10051           TEST_DrawLevelField(x, y + 1);
10052       }
10053
10054       Feld[x][y] = Store[x][y];
10055       Store[x][y] = 0;
10056       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10057       TEST_DrawLevelField(x, y);
10058     }
10059   }
10060 }
10061
10062 void MauerAbleger(int ax, int ay)
10063 {
10064   int element = Feld[ax][ay];
10065   int graphic = el2img(element);
10066   boolean oben_frei = FALSE, unten_frei = FALSE;
10067   boolean links_frei = FALSE, rechts_frei = FALSE;
10068   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10069   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10070   boolean new_wall = FALSE;
10071
10072   if (IS_ANIMATED(graphic))
10073     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10074
10075   if (!MovDelay[ax][ay])        /* start building new wall */
10076     MovDelay[ax][ay] = 6;
10077
10078   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10079   {
10080     MovDelay[ax][ay]--;
10081     if (MovDelay[ax][ay])
10082       return;
10083   }
10084
10085   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10086     oben_frei = TRUE;
10087   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10088     unten_frei = TRUE;
10089   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10090     links_frei = TRUE;
10091   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10092     rechts_frei = TRUE;
10093
10094   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10095       element == EL_EXPANDABLE_WALL_ANY)
10096   {
10097     if (oben_frei)
10098     {
10099       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10100       Store[ax][ay-1] = element;
10101       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10102       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10103         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10104                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10105       new_wall = TRUE;
10106     }
10107     if (unten_frei)
10108     {
10109       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10110       Store[ax][ay+1] = element;
10111       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10112       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10113         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10114                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10115       new_wall = TRUE;
10116     }
10117   }
10118
10119   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10120       element == EL_EXPANDABLE_WALL_ANY ||
10121       element == EL_EXPANDABLE_WALL ||
10122       element == EL_BD_EXPANDABLE_WALL)
10123   {
10124     if (links_frei)
10125     {
10126       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10127       Store[ax-1][ay] = element;
10128       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10129       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10130         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10131                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10132       new_wall = TRUE;
10133     }
10134
10135     if (rechts_frei)
10136     {
10137       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10138       Store[ax+1][ay] = element;
10139       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10140       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10141         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10142                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10143       new_wall = TRUE;
10144     }
10145   }
10146
10147   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10148     TEST_DrawLevelField(ax, ay);
10149
10150   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10151     oben_massiv = TRUE;
10152   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10153     unten_massiv = TRUE;
10154   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10155     links_massiv = TRUE;
10156   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10157     rechts_massiv = TRUE;
10158
10159   if (((oben_massiv && unten_massiv) ||
10160        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10161        element == EL_EXPANDABLE_WALL) &&
10162       ((links_massiv && rechts_massiv) ||
10163        element == EL_EXPANDABLE_WALL_VERTICAL))
10164     Feld[ax][ay] = EL_WALL;
10165
10166   if (new_wall)
10167     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10168 }
10169
10170 void MauerAblegerStahl(int ax, int ay)
10171 {
10172   int element = Feld[ax][ay];
10173   int graphic = el2img(element);
10174   boolean oben_frei = FALSE, unten_frei = FALSE;
10175   boolean links_frei = FALSE, rechts_frei = FALSE;
10176   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10177   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10178   boolean new_wall = FALSE;
10179
10180   if (IS_ANIMATED(graphic))
10181     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10182
10183   if (!MovDelay[ax][ay])        /* start building new wall */
10184     MovDelay[ax][ay] = 6;
10185
10186   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10187   {
10188     MovDelay[ax][ay]--;
10189     if (MovDelay[ax][ay])
10190       return;
10191   }
10192
10193   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10194     oben_frei = TRUE;
10195   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10196     unten_frei = TRUE;
10197   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10198     links_frei = TRUE;
10199   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10200     rechts_frei = TRUE;
10201
10202   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10203       element == EL_EXPANDABLE_STEELWALL_ANY)
10204   {
10205     if (oben_frei)
10206     {
10207       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10208       Store[ax][ay-1] = element;
10209       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10210       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10211         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10212                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10213       new_wall = TRUE;
10214     }
10215     if (unten_frei)
10216     {
10217       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10218       Store[ax][ay+1] = element;
10219       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10220       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10221         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10222                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10223       new_wall = TRUE;
10224     }
10225   }
10226
10227   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10228       element == EL_EXPANDABLE_STEELWALL_ANY)
10229   {
10230     if (links_frei)
10231     {
10232       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10233       Store[ax-1][ay] = element;
10234       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10235       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10236         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10237                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10238       new_wall = TRUE;
10239     }
10240
10241     if (rechts_frei)
10242     {
10243       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10244       Store[ax+1][ay] = element;
10245       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10246       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10247         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10248                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10249       new_wall = TRUE;
10250     }
10251   }
10252
10253   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10254     oben_massiv = TRUE;
10255   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10256     unten_massiv = TRUE;
10257   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10258     links_massiv = TRUE;
10259   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10260     rechts_massiv = TRUE;
10261
10262   if (((oben_massiv && unten_massiv) ||
10263        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10264       ((links_massiv && rechts_massiv) ||
10265        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10266     Feld[ax][ay] = EL_STEELWALL;
10267
10268   if (new_wall)
10269     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10270 }
10271
10272 void CheckForDragon(int x, int y)
10273 {
10274   int i, j;
10275   boolean dragon_found = FALSE;
10276   static int xy[4][2] =
10277   {
10278     { 0, -1 },
10279     { -1, 0 },
10280     { +1, 0 },
10281     { 0, +1 }
10282   };
10283
10284   for (i = 0; i < NUM_DIRECTIONS; i++)
10285   {
10286     for (j = 0; j < 4; j++)
10287     {
10288       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10289
10290       if (IN_LEV_FIELD(xx, yy) &&
10291           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10292       {
10293         if (Feld[xx][yy] == EL_DRAGON)
10294           dragon_found = TRUE;
10295       }
10296       else
10297         break;
10298     }
10299   }
10300
10301   if (!dragon_found)
10302   {
10303     for (i = 0; i < NUM_DIRECTIONS; i++)
10304     {
10305       for (j = 0; j < 3; j++)
10306       {
10307         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10308   
10309         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10310         {
10311           Feld[xx][yy] = EL_EMPTY;
10312           TEST_DrawLevelField(xx, yy);
10313         }
10314         else
10315           break;
10316       }
10317     }
10318   }
10319 }
10320
10321 static void InitBuggyBase(int x, int y)
10322 {
10323   int element = Feld[x][y];
10324   int activating_delay = FRAMES_PER_SECOND / 4;
10325
10326   ChangeDelay[x][y] =
10327     (element == EL_SP_BUGGY_BASE ?
10328      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10329      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10330      activating_delay :
10331      element == EL_SP_BUGGY_BASE_ACTIVE ?
10332      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10333 }
10334
10335 static void WarnBuggyBase(int x, int y)
10336 {
10337   int i;
10338   static int xy[4][2] =
10339   {
10340     { 0, -1 },
10341     { -1, 0 },
10342     { +1, 0 },
10343     { 0, +1 }
10344   };
10345
10346   for (i = 0; i < NUM_DIRECTIONS; i++)
10347   {
10348     int xx = x + xy[i][0];
10349     int yy = y + xy[i][1];
10350
10351     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10352     {
10353       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10354
10355       break;
10356     }
10357   }
10358 }
10359
10360 static void InitTrap(int x, int y)
10361 {
10362   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10363 }
10364
10365 static void ActivateTrap(int x, int y)
10366 {
10367   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10368 }
10369
10370 static void ChangeActiveTrap(int x, int y)
10371 {
10372   int graphic = IMG_TRAP_ACTIVE;
10373
10374   /* if new animation frame was drawn, correct crumbled sand border */
10375   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10376     TEST_DrawLevelFieldCrumbled(x, y);
10377 }
10378
10379 static int getSpecialActionElement(int element, int number, int base_element)
10380 {
10381   return (element != EL_EMPTY ? element :
10382           number != -1 ? base_element + number - 1 :
10383           EL_EMPTY);
10384 }
10385
10386 static int getModifiedActionNumber(int value_old, int operator, int operand,
10387                                    int value_min, int value_max)
10388 {
10389   int value_new = (operator == CA_MODE_SET      ? operand :
10390                    operator == CA_MODE_ADD      ? value_old + operand :
10391                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10392                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10393                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10394                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10395                    value_old);
10396
10397   return (value_new < value_min ? value_min :
10398           value_new > value_max ? value_max :
10399           value_new);
10400 }
10401
10402 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10403 {
10404   struct ElementInfo *ei = &element_info[element];
10405   struct ElementChangeInfo *change = &ei->change_page[page];
10406   int target_element = change->target_element;
10407   int action_type = change->action_type;
10408   int action_mode = change->action_mode;
10409   int action_arg = change->action_arg;
10410   int action_element = change->action_element;
10411   int i;
10412
10413   if (!change->has_action)
10414     return;
10415
10416   /* ---------- determine action paramater values -------------------------- */
10417
10418   int level_time_value =
10419     (level.time > 0 ? TimeLeft :
10420      TimePlayed);
10421
10422   int action_arg_element_raw =
10423     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10424      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10425      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10426      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10427      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10428      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10429      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10430      EL_EMPTY);
10431   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10432
10433 #if 0
10434   if (action_arg_element_raw == EL_GROUP_START)
10435     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10436 #endif
10437
10438   int action_arg_direction =
10439     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10440      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10441      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10442      change->actual_trigger_side :
10443      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10444      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10445      MV_NONE);
10446
10447   int action_arg_number_min =
10448     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10449      CA_ARG_MIN);
10450
10451   int action_arg_number_max =
10452     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10453      action_type == CA_SET_LEVEL_GEMS ? 999 :
10454      action_type == CA_SET_LEVEL_TIME ? 9999 :
10455      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10456      action_type == CA_SET_CE_VALUE ? 9999 :
10457      action_type == CA_SET_CE_SCORE ? 9999 :
10458      CA_ARG_MAX);
10459
10460   int action_arg_number_reset =
10461     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10462      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10463      action_type == CA_SET_LEVEL_TIME ? level.time :
10464      action_type == CA_SET_LEVEL_SCORE ? 0 :
10465 #if USE_NEW_CUSTOM_VALUE
10466      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10467 #else
10468      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10469 #endif
10470      action_type == CA_SET_CE_SCORE ? 0 :
10471      0);
10472
10473   int action_arg_number =
10474     (action_arg <= CA_ARG_MAX ? action_arg :
10475      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10476      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10477      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10478      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10479      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10480      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10481 #if USE_NEW_CUSTOM_VALUE
10482      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10483 #else
10484      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10485 #endif
10486      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10487      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10488      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10489      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10490      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10491      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10492      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10493      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10494      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10495      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10496      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10497      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10498      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10499      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10500      -1);
10501
10502   int action_arg_number_old =
10503     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10504      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10505      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10506      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10507      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10508      0);
10509
10510   int action_arg_number_new =
10511     getModifiedActionNumber(action_arg_number_old,
10512                             action_mode, action_arg_number,
10513                             action_arg_number_min, action_arg_number_max);
10514
10515 #if 1
10516   int trigger_player_bits =
10517     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10518      change->actual_trigger_player_bits : change->trigger_player);
10519 #else
10520   int trigger_player_bits =
10521     (change->actual_trigger_player >= EL_PLAYER_1 &&
10522      change->actual_trigger_player <= EL_PLAYER_4 ?
10523      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10524      PLAYER_BITS_ANY);
10525 #endif
10526
10527   int action_arg_player_bits =
10528     (action_arg >= CA_ARG_PLAYER_1 &&
10529      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10530      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10531      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10532      PLAYER_BITS_ANY);
10533
10534   /* ---------- execute action  -------------------------------------------- */
10535
10536   switch (action_type)
10537   {
10538     case CA_NO_ACTION:
10539     {
10540       return;
10541     }
10542
10543     /* ---------- level actions  ------------------------------------------- */
10544
10545     case CA_RESTART_LEVEL:
10546     {
10547       game.restart_level = TRUE;
10548
10549       break;
10550     }
10551
10552     case CA_SHOW_ENVELOPE:
10553     {
10554       int element = getSpecialActionElement(action_arg_element,
10555                                             action_arg_number, EL_ENVELOPE_1);
10556
10557       if (IS_ENVELOPE(element))
10558         local_player->show_envelope = element;
10559
10560       break;
10561     }
10562
10563     case CA_SET_LEVEL_TIME:
10564     {
10565       if (level.time > 0)       /* only modify limited time value */
10566       {
10567         TimeLeft = action_arg_number_new;
10568
10569 #if 1
10570         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10571
10572         DisplayGameControlValues();
10573 #else
10574         DrawGameValue_Time(TimeLeft);
10575 #endif
10576
10577         if (!TimeLeft && setup.time_limit)
10578           for (i = 0; i < MAX_PLAYERS; i++)
10579             KillPlayer(&stored_player[i]);
10580       }
10581
10582       break;
10583     }
10584
10585     case CA_SET_LEVEL_SCORE:
10586     {
10587       local_player->score = action_arg_number_new;
10588
10589 #if 1
10590       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10591
10592       DisplayGameControlValues();
10593 #else
10594       DrawGameValue_Score(local_player->score);
10595 #endif
10596
10597       break;
10598     }
10599
10600     case CA_SET_LEVEL_GEMS:
10601     {
10602       local_player->gems_still_needed = action_arg_number_new;
10603
10604 #if 1
10605       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10606
10607       DisplayGameControlValues();
10608 #else
10609       DrawGameValue_Emeralds(local_player->gems_still_needed);
10610 #endif
10611
10612       break;
10613     }
10614
10615 #if !USE_PLAYER_GRAVITY
10616     case CA_SET_LEVEL_GRAVITY:
10617     {
10618       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10619                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10620                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10621                       game.gravity);
10622       break;
10623     }
10624 #endif
10625
10626     case CA_SET_LEVEL_WIND:
10627     {
10628       game.wind_direction = action_arg_direction;
10629
10630       break;
10631     }
10632
10633     case CA_SET_LEVEL_RANDOM_SEED:
10634     {
10635 #if 1
10636       /* ensure that setting a new random seed while playing is predictable */
10637       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10638 #else
10639       InitRND(action_arg_number_new);
10640 #endif
10641
10642 #if 0
10643       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10644 #endif
10645
10646 #if 0
10647       {
10648         int i;
10649
10650         printf("::: ");
10651         for (i = 0; i < 9; i++)
10652           printf("%d, ", RND(2));
10653         printf("\n");
10654       }
10655 #endif
10656
10657       break;
10658     }
10659
10660     /* ---------- player actions  ------------------------------------------ */
10661
10662     case CA_MOVE_PLAYER:
10663     {
10664       /* automatically move to the next field in specified direction */
10665       for (i = 0; i < MAX_PLAYERS; i++)
10666         if (trigger_player_bits & (1 << i))
10667           stored_player[i].programmed_action = action_arg_direction;
10668
10669       break;
10670     }
10671
10672     case CA_EXIT_PLAYER:
10673     {
10674       for (i = 0; i < MAX_PLAYERS; i++)
10675         if (action_arg_player_bits & (1 << i))
10676           PlayerWins(&stored_player[i]);
10677
10678       break;
10679     }
10680
10681     case CA_KILL_PLAYER:
10682     {
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684         if (action_arg_player_bits & (1 << i))
10685           KillPlayer(&stored_player[i]);
10686
10687       break;
10688     }
10689
10690     case CA_SET_PLAYER_KEYS:
10691     {
10692       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10693       int element = getSpecialActionElement(action_arg_element,
10694                                             action_arg_number, EL_KEY_1);
10695
10696       if (IS_KEY(element))
10697       {
10698         for (i = 0; i < MAX_PLAYERS; i++)
10699         {
10700           if (trigger_player_bits & (1 << i))
10701           {
10702             stored_player[i].key[KEY_NR(element)] = key_state;
10703
10704             DrawGameDoorValues();
10705           }
10706         }
10707       }
10708
10709       break;
10710     }
10711
10712     case CA_SET_PLAYER_SPEED:
10713     {
10714 #if 0
10715       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10716 #endif
10717
10718       for (i = 0; i < MAX_PLAYERS; i++)
10719       {
10720         if (trigger_player_bits & (1 << i))
10721         {
10722           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10723
10724           if (action_arg == CA_ARG_SPEED_FASTER &&
10725               stored_player[i].cannot_move)
10726           {
10727             action_arg_number = STEPSIZE_VERY_SLOW;
10728           }
10729           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10730                    action_arg == CA_ARG_SPEED_FASTER)
10731           {
10732             action_arg_number = 2;
10733             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10734                            CA_MODE_MULTIPLY);
10735           }
10736           else if (action_arg == CA_ARG_NUMBER_RESET)
10737           {
10738             action_arg_number = level.initial_player_stepsize[i];
10739           }
10740
10741           move_stepsize =
10742             getModifiedActionNumber(move_stepsize,
10743                                     action_mode,
10744                                     action_arg_number,
10745                                     action_arg_number_min,
10746                                     action_arg_number_max);
10747
10748           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10749         }
10750       }
10751
10752       break;
10753     }
10754
10755     case CA_SET_PLAYER_SHIELD:
10756     {
10757       for (i = 0; i < MAX_PLAYERS; i++)
10758       {
10759         if (trigger_player_bits & (1 << i))
10760         {
10761           if (action_arg == CA_ARG_SHIELD_OFF)
10762           {
10763             stored_player[i].shield_normal_time_left = 0;
10764             stored_player[i].shield_deadly_time_left = 0;
10765           }
10766           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10767           {
10768             stored_player[i].shield_normal_time_left = 999999;
10769           }
10770           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10771           {
10772             stored_player[i].shield_normal_time_left = 999999;
10773             stored_player[i].shield_deadly_time_left = 999999;
10774           }
10775         }
10776       }
10777
10778       break;
10779     }
10780
10781 #if USE_PLAYER_GRAVITY
10782     case CA_SET_PLAYER_GRAVITY:
10783     {
10784       for (i = 0; i < MAX_PLAYERS; i++)
10785       {
10786         if (trigger_player_bits & (1 << i))
10787         {
10788           stored_player[i].gravity =
10789             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10790              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10791              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10792              stored_player[i].gravity);
10793         }
10794       }
10795
10796       break;
10797     }
10798 #endif
10799
10800     case CA_SET_PLAYER_ARTWORK:
10801     {
10802       for (i = 0; i < MAX_PLAYERS; i++)
10803       {
10804         if (trigger_player_bits & (1 << i))
10805         {
10806           int artwork_element = action_arg_element;
10807
10808           if (action_arg == CA_ARG_ELEMENT_RESET)
10809             artwork_element =
10810               (level.use_artwork_element[i] ? level.artwork_element[i] :
10811                stored_player[i].element_nr);
10812
10813 #if USE_GFX_RESET_PLAYER_ARTWORK
10814           if (stored_player[i].artwork_element != artwork_element)
10815             stored_player[i].Frame = 0;
10816 #endif
10817
10818           stored_player[i].artwork_element = artwork_element;
10819
10820           SetPlayerWaiting(&stored_player[i], FALSE);
10821
10822           /* set number of special actions for bored and sleeping animation */
10823           stored_player[i].num_special_action_bored =
10824             get_num_special_action(artwork_element,
10825                                    ACTION_BORING_1, ACTION_BORING_LAST);
10826           stored_player[i].num_special_action_sleeping =
10827             get_num_special_action(artwork_element,
10828                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10829         }
10830       }
10831
10832       break;
10833     }
10834
10835     case CA_SET_PLAYER_INVENTORY:
10836     {
10837       for (i = 0; i < MAX_PLAYERS; i++)
10838       {
10839         struct PlayerInfo *player = &stored_player[i];
10840         int j, k;
10841
10842         if (trigger_player_bits & (1 << i))
10843         {
10844           int inventory_element = action_arg_element;
10845
10846           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10847               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10848               action_arg == CA_ARG_ELEMENT_ACTION)
10849           {
10850             int element = inventory_element;
10851             int collect_count = element_info[element].collect_count_initial;
10852
10853             if (!IS_CUSTOM_ELEMENT(element))
10854               collect_count = 1;
10855
10856             if (collect_count == 0)
10857               player->inventory_infinite_element = element;
10858             else
10859               for (k = 0; k < collect_count; k++)
10860                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10861                   player->inventory_element[player->inventory_size++] =
10862                     element;
10863           }
10864           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10865                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10866                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10867           {
10868             if (player->inventory_infinite_element != EL_UNDEFINED &&
10869                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10870                                      action_arg_element_raw))
10871               player->inventory_infinite_element = EL_UNDEFINED;
10872
10873             for (k = 0, j = 0; j < player->inventory_size; j++)
10874             {
10875               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10876                                         action_arg_element_raw))
10877                 player->inventory_element[k++] = player->inventory_element[j];
10878             }
10879
10880             player->inventory_size = k;
10881           }
10882           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10883           {
10884             if (player->inventory_size > 0)
10885             {
10886               for (j = 0; j < player->inventory_size - 1; j++)
10887                 player->inventory_element[j] = player->inventory_element[j + 1];
10888
10889               player->inventory_size--;
10890             }
10891           }
10892           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10893           {
10894             if (player->inventory_size > 0)
10895               player->inventory_size--;
10896           }
10897           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10898           {
10899             player->inventory_infinite_element = EL_UNDEFINED;
10900             player->inventory_size = 0;
10901           }
10902           else if (action_arg == CA_ARG_INVENTORY_RESET)
10903           {
10904             player->inventory_infinite_element = EL_UNDEFINED;
10905             player->inventory_size = 0;
10906
10907             if (level.use_initial_inventory[i])
10908             {
10909               for (j = 0; j < level.initial_inventory_size[i]; j++)
10910               {
10911                 int element = level.initial_inventory_content[i][j];
10912                 int collect_count = element_info[element].collect_count_initial;
10913
10914                 if (!IS_CUSTOM_ELEMENT(element))
10915                   collect_count = 1;
10916
10917                 if (collect_count == 0)
10918                   player->inventory_infinite_element = element;
10919                 else
10920                   for (k = 0; k < collect_count; k++)
10921                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10922                       player->inventory_element[player->inventory_size++] =
10923                         element;
10924               }
10925             }
10926           }
10927         }
10928       }
10929
10930       break;
10931     }
10932
10933     /* ---------- CE actions  ---------------------------------------------- */
10934
10935     case CA_SET_CE_VALUE:
10936     {
10937 #if USE_NEW_CUSTOM_VALUE
10938       int last_ce_value = CustomValue[x][y];
10939
10940       CustomValue[x][y] = action_arg_number_new;
10941
10942       if (CustomValue[x][y] != last_ce_value)
10943       {
10944         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10945         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10946
10947         if (CustomValue[x][y] == 0)
10948         {
10949           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10950           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10951         }
10952       }
10953 #endif
10954
10955       break;
10956     }
10957
10958     case CA_SET_CE_SCORE:
10959     {
10960 #if USE_NEW_CUSTOM_VALUE
10961       int last_ce_score = ei->collect_score;
10962
10963       ei->collect_score = action_arg_number_new;
10964
10965       if (ei->collect_score != last_ce_score)
10966       {
10967         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10968         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10969
10970         if (ei->collect_score == 0)
10971         {
10972           int xx, yy;
10973
10974           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10975           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10976
10977           /*
10978             This is a very special case that seems to be a mixture between
10979             CheckElementChange() and CheckTriggeredElementChange(): while
10980             the first one only affects single elements that are triggered
10981             directly, the second one affects multiple elements in the playfield
10982             that are triggered indirectly by another element. This is a third
10983             case: Changing the CE score always affects multiple identical CEs,
10984             so every affected CE must be checked, not only the single CE for
10985             which the CE score was changed in the first place (as every instance
10986             of that CE shares the same CE score, and therefore also can change)!
10987           */
10988           SCAN_PLAYFIELD(xx, yy)
10989           {
10990             if (Feld[xx][yy] == element)
10991               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10992                                  CE_SCORE_GETS_ZERO);
10993           }
10994         }
10995       }
10996 #endif
10997
10998       break;
10999     }
11000
11001     case CA_SET_CE_ARTWORK:
11002     {
11003       int artwork_element = action_arg_element;
11004       boolean reset_frame = FALSE;
11005       int xx, yy;
11006
11007       if (action_arg == CA_ARG_ELEMENT_RESET)
11008         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11009                            element);
11010
11011       if (ei->gfx_element != artwork_element)
11012         reset_frame = TRUE;
11013
11014       ei->gfx_element = artwork_element;
11015
11016       SCAN_PLAYFIELD(xx, yy)
11017       {
11018         if (Feld[xx][yy] == element)
11019         {
11020           if (reset_frame)
11021           {
11022             ResetGfxAnimation(xx, yy);
11023             ResetRandomAnimationValue(xx, yy);
11024           }
11025
11026           TEST_DrawLevelField(xx, yy);
11027         }
11028       }
11029
11030       break;
11031     }
11032
11033     /* ---------- engine actions  ------------------------------------------ */
11034
11035     case CA_SET_ENGINE_SCAN_MODE:
11036     {
11037       InitPlayfieldScanMode(action_arg);
11038
11039       break;
11040     }
11041
11042     default:
11043       break;
11044   }
11045 }
11046
11047 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11048 {
11049   int old_element = Feld[x][y];
11050   int new_element = GetElementFromGroupElement(element);
11051   int previous_move_direction = MovDir[x][y];
11052 #if USE_NEW_CUSTOM_VALUE
11053   int last_ce_value = CustomValue[x][y];
11054 #endif
11055   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11056   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11057   boolean add_player_onto_element = (new_element_is_player &&
11058 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11059                                      /* this breaks SnakeBite when a snake is
11060                                         halfway through a door that closes */
11061                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11062                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11063 #endif
11064                                      IS_WALKABLE(old_element));
11065
11066 #if 0
11067   /* check if element under the player changes from accessible to unaccessible
11068      (needed for special case of dropping element which then changes) */
11069   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11070       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11071   {
11072     Bang(x, y);
11073
11074     return;
11075   }
11076 #endif
11077
11078   if (!add_player_onto_element)
11079   {
11080     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11081       RemoveMovingField(x, y);
11082     else
11083       RemoveField(x, y);
11084
11085     Feld[x][y] = new_element;
11086
11087 #if !USE_GFX_RESET_GFX_ANIMATION
11088     ResetGfxAnimation(x, y);
11089     ResetRandomAnimationValue(x, y);
11090 #endif
11091
11092     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11093       MovDir[x][y] = previous_move_direction;
11094
11095 #if USE_NEW_CUSTOM_VALUE
11096     if (element_info[new_element].use_last_ce_value)
11097       CustomValue[x][y] = last_ce_value;
11098 #endif
11099
11100     InitField_WithBug1(x, y, FALSE);
11101
11102     new_element = Feld[x][y];   /* element may have changed */
11103
11104 #if USE_GFX_RESET_GFX_ANIMATION
11105     ResetGfxAnimation(x, y);
11106     ResetRandomAnimationValue(x, y);
11107 #endif
11108
11109     TEST_DrawLevelField(x, y);
11110
11111     if (GFX_CRUMBLED(new_element))
11112       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11113   }
11114
11115 #if 1
11116   /* check if element under the player changes from accessible to unaccessible
11117      (needed for special case of dropping element which then changes) */
11118   /* (must be checked after creating new element for walkable group elements) */
11119 #if USE_FIX_KILLED_BY_NON_WALKABLE
11120   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11121       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11122   {
11123     Bang(x, y);
11124
11125     return;
11126   }
11127 #else
11128   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11129       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11130   {
11131     Bang(x, y);
11132
11133     return;
11134   }
11135 #endif
11136 #endif
11137
11138   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11139   if (new_element_is_player)
11140     RelocatePlayer(x, y, new_element);
11141
11142   if (is_change)
11143     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11144
11145   TestIfBadThingTouchesPlayer(x, y);
11146   TestIfPlayerTouchesCustomElement(x, y);
11147   TestIfElementTouchesCustomElement(x, y);
11148 }
11149
11150 static void CreateField(int x, int y, int element)
11151 {
11152   CreateFieldExt(x, y, element, FALSE);
11153 }
11154
11155 static void CreateElementFromChange(int x, int y, int element)
11156 {
11157   element = GET_VALID_RUNTIME_ELEMENT(element);
11158
11159 #if USE_STOP_CHANGED_ELEMENTS
11160   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11161   {
11162     int old_element = Feld[x][y];
11163
11164     /* prevent changed element from moving in same engine frame
11165        unless both old and new element can either fall or move */
11166     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11167         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11168       Stop[x][y] = TRUE;
11169   }
11170 #endif
11171
11172   CreateFieldExt(x, y, element, TRUE);
11173 }
11174
11175 static boolean ChangeElement(int x, int y, int element, int page)
11176 {
11177   struct ElementInfo *ei = &element_info[element];
11178   struct ElementChangeInfo *change = &ei->change_page[page];
11179   int ce_value = CustomValue[x][y];
11180   int ce_score = ei->collect_score;
11181   int target_element;
11182   int old_element = Feld[x][y];
11183
11184   /* always use default change event to prevent running into a loop */
11185   if (ChangeEvent[x][y] == -1)
11186     ChangeEvent[x][y] = CE_DELAY;
11187
11188   if (ChangeEvent[x][y] == CE_DELAY)
11189   {
11190     /* reset actual trigger element, trigger player and action element */
11191     change->actual_trigger_element = EL_EMPTY;
11192     change->actual_trigger_player = EL_EMPTY;
11193     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11194     change->actual_trigger_side = CH_SIDE_NONE;
11195     change->actual_trigger_ce_value = 0;
11196     change->actual_trigger_ce_score = 0;
11197   }
11198
11199   /* do not change elements more than a specified maximum number of changes */
11200   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11201     return FALSE;
11202
11203   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11204
11205   if (change->explode)
11206   {
11207     Bang(x, y);
11208
11209     return TRUE;
11210   }
11211
11212   if (change->use_target_content)
11213   {
11214     boolean complete_replace = TRUE;
11215     boolean can_replace[3][3];
11216     int xx, yy;
11217
11218     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11219     {
11220       boolean is_empty;
11221       boolean is_walkable;
11222       boolean is_diggable;
11223       boolean is_collectible;
11224       boolean is_removable;
11225       boolean is_destructible;
11226       int ex = x + xx - 1;
11227       int ey = y + yy - 1;
11228       int content_element = change->target_content.e[xx][yy];
11229       int e;
11230
11231       can_replace[xx][yy] = TRUE;
11232
11233       if (ex == x && ey == y)   /* do not check changing element itself */
11234         continue;
11235
11236       if (content_element == EL_EMPTY_SPACE)
11237       {
11238         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11239
11240         continue;
11241       }
11242
11243       if (!IN_LEV_FIELD(ex, ey))
11244       {
11245         can_replace[xx][yy] = FALSE;
11246         complete_replace = FALSE;
11247
11248         continue;
11249       }
11250
11251       e = Feld[ex][ey];
11252
11253       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11254         e = MovingOrBlocked2Element(ex, ey);
11255
11256       is_empty = (IS_FREE(ex, ey) ||
11257                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11258
11259       is_walkable     = (is_empty || IS_WALKABLE(e));
11260       is_diggable     = (is_empty || IS_DIGGABLE(e));
11261       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11262       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11263       is_removable    = (is_diggable || is_collectible);
11264
11265       can_replace[xx][yy] =
11266         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11267           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11268           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11269           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11270           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11271           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11272          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11273
11274       if (!can_replace[xx][yy])
11275         complete_replace = FALSE;
11276     }
11277
11278     if (!change->only_if_complete || complete_replace)
11279     {
11280       boolean something_has_changed = FALSE;
11281
11282       if (change->only_if_complete && change->use_random_replace &&
11283           RND(100) < change->random_percentage)
11284         return FALSE;
11285
11286       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11287       {
11288         int ex = x + xx - 1;
11289         int ey = y + yy - 1;
11290         int content_element;
11291
11292         if (can_replace[xx][yy] && (!change->use_random_replace ||
11293                                     RND(100) < change->random_percentage))
11294         {
11295           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11296             RemoveMovingField(ex, ey);
11297
11298           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11299
11300           content_element = change->target_content.e[xx][yy];
11301           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11302                                               ce_value, ce_score);
11303
11304           CreateElementFromChange(ex, ey, target_element);
11305
11306           something_has_changed = TRUE;
11307
11308           /* for symmetry reasons, freeze newly created border elements */
11309           if (ex != x || ey != y)
11310             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11311         }
11312       }
11313
11314       if (something_has_changed)
11315       {
11316         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11317         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11318       }
11319     }
11320   }
11321   else
11322   {
11323     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11324                                         ce_value, ce_score);
11325
11326     if (element == EL_DIAGONAL_GROWING ||
11327         element == EL_DIAGONAL_SHRINKING)
11328     {
11329       target_element = Store[x][y];
11330
11331       Store[x][y] = EL_EMPTY;
11332     }
11333
11334     CreateElementFromChange(x, y, target_element);
11335
11336     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11337     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11338   }
11339
11340   /* this uses direct change before indirect change */
11341   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11342
11343   return TRUE;
11344 }
11345
11346 #if USE_NEW_DELAYED_ACTION
11347
11348 static void HandleElementChange(int x, int y, int page)
11349 {
11350   int element = MovingOrBlocked2Element(x, y);
11351   struct ElementInfo *ei = &element_info[element];
11352   struct ElementChangeInfo *change = &ei->change_page[page];
11353   boolean handle_action_before_change = FALSE;
11354
11355 #ifdef DEBUG
11356   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11357       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11358   {
11359     printf("\n\n");
11360     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11361            x, y, element, element_info[element].token_name);
11362     printf("HandleElementChange(): This should never happen!\n");
11363     printf("\n\n");
11364   }
11365 #endif
11366
11367   /* this can happen with classic bombs on walkable, changing elements */
11368   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11369   {
11370 #if 0
11371     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11372       ChangeDelay[x][y] = 0;
11373 #endif
11374
11375     return;
11376   }
11377
11378   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11379   {
11380     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11381
11382     if (change->can_change)
11383     {
11384 #if 1
11385       /* !!! not clear why graphic animation should be reset at all here !!! */
11386       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11387 #if USE_GFX_RESET_WHEN_NOT_MOVING
11388       /* when a custom element is about to change (for example by change delay),
11389          do not reset graphic animation when the custom element is moving */
11390       if (!IS_MOVING(x, y))
11391 #endif
11392       {
11393         ResetGfxAnimation(x, y);
11394         ResetRandomAnimationValue(x, y);
11395       }
11396 #endif
11397
11398       if (change->pre_change_function)
11399         change->pre_change_function(x, y);
11400     }
11401   }
11402
11403   ChangeDelay[x][y]--;
11404
11405   if (ChangeDelay[x][y] != 0)           /* continue element change */
11406   {
11407     if (change->can_change)
11408     {
11409       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11410
11411       if (IS_ANIMATED(graphic))
11412         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11413
11414       if (change->change_function)
11415         change->change_function(x, y);
11416     }
11417   }
11418   else                                  /* finish element change */
11419   {
11420     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11421     {
11422       page = ChangePage[x][y];
11423       ChangePage[x][y] = -1;
11424
11425       change = &ei->change_page[page];
11426     }
11427
11428     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11429     {
11430       ChangeDelay[x][y] = 1;            /* try change after next move step */
11431       ChangePage[x][y] = page;          /* remember page to use for change */
11432
11433       return;
11434     }
11435
11436 #if 1
11437     /* special case: set new level random seed before changing element */
11438     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11439       handle_action_before_change = TRUE;
11440
11441     if (change->has_action && handle_action_before_change)
11442       ExecuteCustomElementAction(x, y, element, page);
11443 #endif
11444
11445     if (change->can_change)
11446     {
11447       if (ChangeElement(x, y, element, page))
11448       {
11449         if (change->post_change_function)
11450           change->post_change_function(x, y);
11451       }
11452     }
11453
11454     if (change->has_action && !handle_action_before_change)
11455       ExecuteCustomElementAction(x, y, element, page);
11456   }
11457 }
11458
11459 #else
11460
11461 static void HandleElementChange(int x, int y, int page)
11462 {
11463   int element = MovingOrBlocked2Element(x, y);
11464   struct ElementInfo *ei = &element_info[element];
11465   struct ElementChangeInfo *change = &ei->change_page[page];
11466
11467 #ifdef DEBUG
11468   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11469   {
11470     printf("\n\n");
11471     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11472            x, y, element, element_info[element].token_name);
11473     printf("HandleElementChange(): This should never happen!\n");
11474     printf("\n\n");
11475   }
11476 #endif
11477
11478   /* this can happen with classic bombs on walkable, changing elements */
11479   if (!CAN_CHANGE(element))
11480   {
11481 #if 0
11482     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11483       ChangeDelay[x][y] = 0;
11484 #endif
11485
11486     return;
11487   }
11488
11489   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11490   {
11491     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11492
11493     ResetGfxAnimation(x, y);
11494     ResetRandomAnimationValue(x, y);
11495
11496     if (change->pre_change_function)
11497       change->pre_change_function(x, y);
11498   }
11499
11500   ChangeDelay[x][y]--;
11501
11502   if (ChangeDelay[x][y] != 0)           /* continue element change */
11503   {
11504     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11505
11506     if (IS_ANIMATED(graphic))
11507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11508
11509     if (change->change_function)
11510       change->change_function(x, y);
11511   }
11512   else                                  /* finish element change */
11513   {
11514     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11515     {
11516       page = ChangePage[x][y];
11517       ChangePage[x][y] = -1;
11518
11519       change = &ei->change_page[page];
11520     }
11521
11522     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11523     {
11524       ChangeDelay[x][y] = 1;            /* try change after next move step */
11525       ChangePage[x][y] = page;          /* remember page to use for change */
11526
11527       return;
11528     }
11529
11530     if (ChangeElement(x, y, element, page))
11531     {
11532       if (change->post_change_function)
11533         change->post_change_function(x, y);
11534     }
11535   }
11536 }
11537
11538 #endif
11539
11540 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11541                                               int trigger_element,
11542                                               int trigger_event,
11543                                               int trigger_player,
11544                                               int trigger_side,
11545                                               int trigger_page)
11546 {
11547   boolean change_done_any = FALSE;
11548   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11549   int i;
11550
11551   if (!(trigger_events[trigger_element][trigger_event]))
11552     return FALSE;
11553
11554 #if 0
11555   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11556          trigger_event, recursion_loop_depth, recursion_loop_detected,
11557          recursion_loop_element, EL_NAME(recursion_loop_element));
11558 #endif
11559
11560   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11561
11562   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11563   {
11564     int element = EL_CUSTOM_START + i;
11565     boolean change_done = FALSE;
11566     int p;
11567
11568     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11569         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11570       continue;
11571
11572     for (p = 0; p < element_info[element].num_change_pages; p++)
11573     {
11574       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11575
11576       if (change->can_change_or_has_action &&
11577           change->has_event[trigger_event] &&
11578           change->trigger_side & trigger_side &&
11579           change->trigger_player & trigger_player &&
11580           change->trigger_page & trigger_page_bits &&
11581           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11582       {
11583         change->actual_trigger_element = trigger_element;
11584         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11585         change->actual_trigger_player_bits = trigger_player;
11586         change->actual_trigger_side = trigger_side;
11587         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11588         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11589
11590 #if 0
11591         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11592                element, EL_NAME(element), p);
11593 #endif
11594
11595         if ((change->can_change && !change_done) || change->has_action)
11596         {
11597           int x, y;
11598
11599           SCAN_PLAYFIELD(x, y)
11600           {
11601             if (Feld[x][y] == element)
11602             {
11603               if (change->can_change && !change_done)
11604               {
11605 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11606                 /* if element already changed in this frame, not only prevent
11607                    another element change (checked in ChangeElement()), but
11608                    also prevent additional element actions for this element */
11609
11610                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11611                     !level.use_action_after_change_bug)
11612                   continue;
11613 #endif
11614
11615 #if 0
11616                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11617                        element, EL_NAME(element), p);
11618 #endif
11619
11620                 ChangeDelay[x][y] = 1;
11621                 ChangeEvent[x][y] = trigger_event;
11622
11623                 HandleElementChange(x, y, p);
11624               }
11625 #if USE_NEW_DELAYED_ACTION
11626               else if (change->has_action)
11627               {
11628 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11629                 /* if element already changed in this frame, not only prevent
11630                    another element change (checked in ChangeElement()), but
11631                    also prevent additional element actions for this element */
11632
11633                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11634                     !level.use_action_after_change_bug)
11635                   continue;
11636 #endif
11637
11638
11639 #if 0
11640                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11641                        element, EL_NAME(element), p);
11642 #endif
11643
11644                 ExecuteCustomElementAction(x, y, element, p);
11645                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11646               }
11647 #else
11648               if (change->has_action)
11649               {
11650                 ExecuteCustomElementAction(x, y, element, p);
11651                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11652               }
11653 #endif
11654             }
11655           }
11656
11657           if (change->can_change)
11658           {
11659             change_done = TRUE;
11660             change_done_any = TRUE;
11661
11662 #if 0
11663             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11664                    element, EL_NAME(element), p);
11665 #endif
11666
11667           }
11668         }
11669       }
11670     }
11671   }
11672
11673   RECURSION_LOOP_DETECTION_END();
11674
11675   return change_done_any;
11676 }
11677
11678 static boolean CheckElementChangeExt(int x, int y,
11679                                      int element,
11680                                      int trigger_element,
11681                                      int trigger_event,
11682                                      int trigger_player,
11683                                      int trigger_side)
11684 {
11685   boolean change_done = FALSE;
11686   int p;
11687
11688   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11689       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11690     return FALSE;
11691
11692   if (Feld[x][y] == EL_BLOCKED)
11693   {
11694     Blocked2Moving(x, y, &x, &y);
11695     element = Feld[x][y];
11696   }
11697
11698 #if 0
11699   /* check if element has already changed */
11700   if (Feld[x][y] != element)
11701     return FALSE;
11702 #else
11703   /* check if element has already changed or is about to change after moving */
11704   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11705        Feld[x][y] != element) ||
11706
11707       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11708        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11709         ChangePage[x][y] != -1)))
11710     return FALSE;
11711 #endif
11712
11713 #if 0
11714   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11715          trigger_event, recursion_loop_depth, recursion_loop_detected,
11716          recursion_loop_element, EL_NAME(recursion_loop_element));
11717 #endif
11718
11719   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11720
11721 #if 0
11722   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11723 #endif
11724
11725   for (p = 0; p < element_info[element].num_change_pages; p++)
11726   {
11727     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11728
11729     /* check trigger element for all events where the element that is checked
11730        for changing interacts with a directly adjacent element -- this is
11731        different to element changes that affect other elements to change on the
11732        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11733     boolean check_trigger_element =
11734       (trigger_event == CE_TOUCHING_X ||
11735        trigger_event == CE_HITTING_X ||
11736        trigger_event == CE_HIT_BY_X ||
11737 #if 1
11738        /* this one was forgotten until 3.2.3 */
11739        trigger_event == CE_DIGGING_X);
11740 #endif
11741
11742     if (change->can_change_or_has_action &&
11743         change->has_event[trigger_event] &&
11744         change->trigger_side & trigger_side &&
11745         change->trigger_player & trigger_player &&
11746         (!check_trigger_element ||
11747          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11748     {
11749       change->actual_trigger_element = trigger_element;
11750       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11751       change->actual_trigger_player_bits = trigger_player;
11752       change->actual_trigger_side = trigger_side;
11753       change->actual_trigger_ce_value = CustomValue[x][y];
11754       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11755
11756       /* special case: trigger element not at (x,y) position for some events */
11757       if (check_trigger_element)
11758       {
11759         static struct
11760         {
11761           int dx, dy;
11762         } move_xy[] =
11763           {
11764             {  0,  0 },
11765             { -1,  0 },
11766             { +1,  0 },
11767             {  0,  0 },
11768             {  0, -1 },
11769             {  0,  0 }, { 0, 0 }, { 0, 0 },
11770             {  0, +1 }
11771           };
11772
11773         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11774         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11775
11776         change->actual_trigger_ce_value = CustomValue[xx][yy];
11777         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11778       }
11779
11780       if (change->can_change && !change_done)
11781       {
11782         ChangeDelay[x][y] = 1;
11783         ChangeEvent[x][y] = trigger_event;
11784
11785         HandleElementChange(x, y, p);
11786
11787         change_done = TRUE;
11788       }
11789 #if USE_NEW_DELAYED_ACTION
11790       else if (change->has_action)
11791       {
11792         ExecuteCustomElementAction(x, y, element, p);
11793         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11794       }
11795 #else
11796       if (change->has_action)
11797       {
11798         ExecuteCustomElementAction(x, y, element, p);
11799         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11800       }
11801 #endif
11802     }
11803   }
11804
11805   RECURSION_LOOP_DETECTION_END();
11806
11807   return change_done;
11808 }
11809
11810 static void PlayPlayerSound(struct PlayerInfo *player)
11811 {
11812   int jx = player->jx, jy = player->jy;
11813   int sound_element = player->artwork_element;
11814   int last_action = player->last_action_waiting;
11815   int action = player->action_waiting;
11816
11817   if (player->is_waiting)
11818   {
11819     if (action != last_action)
11820       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11821     else
11822       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11823   }
11824   else
11825   {
11826     if (action != last_action)
11827       StopSound(element_info[sound_element].sound[last_action]);
11828
11829     if (last_action == ACTION_SLEEPING)
11830       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11831   }
11832 }
11833
11834 static void PlayAllPlayersSound()
11835 {
11836   int i;
11837
11838   for (i = 0; i < MAX_PLAYERS; i++)
11839     if (stored_player[i].active)
11840       PlayPlayerSound(&stored_player[i]);
11841 }
11842
11843 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11844 {
11845   boolean last_waiting = player->is_waiting;
11846   int move_dir = player->MovDir;
11847
11848   player->dir_waiting = move_dir;
11849   player->last_action_waiting = player->action_waiting;
11850
11851   if (is_waiting)
11852   {
11853     if (!last_waiting)          /* not waiting -> waiting */
11854     {
11855       player->is_waiting = TRUE;
11856
11857       player->frame_counter_bored =
11858         FrameCounter +
11859         game.player_boring_delay_fixed +
11860         GetSimpleRandom(game.player_boring_delay_random);
11861       player->frame_counter_sleeping =
11862         FrameCounter +
11863         game.player_sleeping_delay_fixed +
11864         GetSimpleRandom(game.player_sleeping_delay_random);
11865
11866       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11867     }
11868
11869     if (game.player_sleeping_delay_fixed +
11870         game.player_sleeping_delay_random > 0 &&
11871         player->anim_delay_counter == 0 &&
11872         player->post_delay_counter == 0 &&
11873         FrameCounter >= player->frame_counter_sleeping)
11874       player->is_sleeping = TRUE;
11875     else if (game.player_boring_delay_fixed +
11876              game.player_boring_delay_random > 0 &&
11877              FrameCounter >= player->frame_counter_bored)
11878       player->is_bored = TRUE;
11879
11880     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11881                               player->is_bored ? ACTION_BORING :
11882                               ACTION_WAITING);
11883
11884     if (player->is_sleeping && player->use_murphy)
11885     {
11886       /* special case for sleeping Murphy when leaning against non-free tile */
11887
11888       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11889           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11890            !IS_MOVING(player->jx - 1, player->jy)))
11891         move_dir = MV_LEFT;
11892       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11893                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11894                 !IS_MOVING(player->jx + 1, player->jy)))
11895         move_dir = MV_RIGHT;
11896       else
11897         player->is_sleeping = FALSE;
11898
11899       player->dir_waiting = move_dir;
11900     }
11901
11902     if (player->is_sleeping)
11903     {
11904       if (player->num_special_action_sleeping > 0)
11905       {
11906         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11907         {
11908           int last_special_action = player->special_action_sleeping;
11909           int num_special_action = player->num_special_action_sleeping;
11910           int special_action =
11911             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11912              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11913              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11914              last_special_action + 1 : ACTION_SLEEPING);
11915           int special_graphic =
11916             el_act_dir2img(player->artwork_element, special_action, move_dir);
11917
11918           player->anim_delay_counter =
11919             graphic_info[special_graphic].anim_delay_fixed +
11920             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11921           player->post_delay_counter =
11922             graphic_info[special_graphic].post_delay_fixed +
11923             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11924
11925           player->special_action_sleeping = special_action;
11926         }
11927
11928         if (player->anim_delay_counter > 0)
11929         {
11930           player->action_waiting = player->special_action_sleeping;
11931           player->anim_delay_counter--;
11932         }
11933         else if (player->post_delay_counter > 0)
11934         {
11935           player->post_delay_counter--;
11936         }
11937       }
11938     }
11939     else if (player->is_bored)
11940     {
11941       if (player->num_special_action_bored > 0)
11942       {
11943         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11944         {
11945           int special_action =
11946             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11947           int special_graphic =
11948             el_act_dir2img(player->artwork_element, special_action, move_dir);
11949
11950           player->anim_delay_counter =
11951             graphic_info[special_graphic].anim_delay_fixed +
11952             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11953           player->post_delay_counter =
11954             graphic_info[special_graphic].post_delay_fixed +
11955             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11956
11957           player->special_action_bored = special_action;
11958         }
11959
11960         if (player->anim_delay_counter > 0)
11961         {
11962           player->action_waiting = player->special_action_bored;
11963           player->anim_delay_counter--;
11964         }
11965         else if (player->post_delay_counter > 0)
11966         {
11967           player->post_delay_counter--;
11968         }
11969       }
11970     }
11971   }
11972   else if (last_waiting)        /* waiting -> not waiting */
11973   {
11974     player->is_waiting = FALSE;
11975     player->is_bored = FALSE;
11976     player->is_sleeping = FALSE;
11977
11978     player->frame_counter_bored = -1;
11979     player->frame_counter_sleeping = -1;
11980
11981     player->anim_delay_counter = 0;
11982     player->post_delay_counter = 0;
11983
11984     player->dir_waiting = player->MovDir;
11985     player->action_waiting = ACTION_DEFAULT;
11986
11987     player->special_action_bored = ACTION_DEFAULT;
11988     player->special_action_sleeping = ACTION_DEFAULT;
11989   }
11990 }
11991
11992 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11993 {
11994   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11995   int left      = player_action & JOY_LEFT;
11996   int right     = player_action & JOY_RIGHT;
11997   int up        = player_action & JOY_UP;
11998   int down      = player_action & JOY_DOWN;
11999   int button1   = player_action & JOY_BUTTON_1;
12000   int button2   = player_action & JOY_BUTTON_2;
12001   int dx        = (left ? -1 : right ? 1 : 0);
12002   int dy        = (up   ? -1 : down  ? 1 : 0);
12003
12004   if (!player->active || tape.pausing)
12005     return 0;
12006
12007   if (player_action)
12008   {
12009     if (button1)
12010       snapped = SnapField(player, dx, dy);
12011     else
12012     {
12013       if (button2)
12014         dropped = DropElement(player);
12015
12016       moved = MovePlayer(player, dx, dy);
12017     }
12018
12019     if (tape.single_step && tape.recording && !tape.pausing)
12020     {
12021 #if 1
12022       /* as it is called "single step mode", just return to pause mode when the
12023          player stopped moving after one tile (or never starts moving at all) */
12024       if (!player->is_moving)
12025 #else
12026       /* this is buggy: there are quite some cases where the single step mode
12027          does not return to pause mode (like pushing things that don't move
12028          or simply by trying to run against a wall) */
12029       if (button1 || (dropped && !moved))
12030 #endif
12031       {
12032         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12033         SnapField(player, 0, 0);                /* stop snapping */
12034       }
12035     }
12036
12037     SetPlayerWaiting(player, FALSE);
12038
12039     return player_action;
12040   }
12041   else
12042   {
12043     /* no actions for this player (no input at player's configured device) */
12044
12045     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12046     SnapField(player, 0, 0);
12047     CheckGravityMovementWhenNotMoving(player);
12048
12049     if (player->MovPos == 0)
12050       SetPlayerWaiting(player, TRUE);
12051
12052     if (player->MovPos == 0)    /* needed for tape.playing */
12053       player->is_moving = FALSE;
12054
12055     player->is_dropping = FALSE;
12056     player->is_dropping_pressed = FALSE;
12057     player->drop_pressed_delay = 0;
12058
12059     return 0;
12060   }
12061 }
12062
12063 static void CheckLevelTime()
12064 {
12065   int i;
12066
12067   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12069   {
12070     if (level.native_em_level->lev->home == 0)  /* all players at home */
12071     {
12072       PlayerWins(local_player);
12073
12074       AllPlayersGone = TRUE;
12075
12076       level.native_em_level->lev->home = -1;
12077     }
12078
12079     if (level.native_em_level->ply[0]->alive == 0 &&
12080         level.native_em_level->ply[1]->alive == 0 &&
12081         level.native_em_level->ply[2]->alive == 0 &&
12082         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12083       AllPlayersGone = TRUE;
12084   }
12085   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12086   {
12087     if (game_sp.LevelSolved &&
12088         !game_sp.GameOver)                              /* game won */
12089     {
12090       PlayerWins(local_player);
12091
12092       game_sp.GameOver = TRUE;
12093
12094       AllPlayersGone = TRUE;
12095     }
12096
12097     if (game_sp.GameOver)                               /* game lost */
12098       AllPlayersGone = TRUE;
12099   }
12100
12101   if (TimeFrames >= FRAMES_PER_SECOND)
12102   {
12103     TimeFrames = 0;
12104     TapeTime++;
12105
12106     for (i = 0; i < MAX_PLAYERS; i++)
12107     {
12108       struct PlayerInfo *player = &stored_player[i];
12109
12110       if (SHIELD_ON(player))
12111       {
12112         player->shield_normal_time_left--;
12113
12114         if (player->shield_deadly_time_left > 0)
12115           player->shield_deadly_time_left--;
12116       }
12117     }
12118
12119     if (!local_player->LevelSolved && !level.use_step_counter)
12120     {
12121       TimePlayed++;
12122
12123       if (TimeLeft > 0)
12124       {
12125         TimeLeft--;
12126
12127         if (TimeLeft <= 10 && setup.time_limit)
12128           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12129
12130 #if 1
12131         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12132
12133         DisplayGameControlValues();
12134 #else
12135         DrawGameValue_Time(TimeLeft);
12136 #endif
12137
12138         if (!TimeLeft && setup.time_limit)
12139         {
12140           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12141             level.native_em_level->lev->killed_out_of_time = TRUE;
12142           else
12143             for (i = 0; i < MAX_PLAYERS; i++)
12144               KillPlayer(&stored_player[i]);
12145         }
12146       }
12147 #if 1
12148       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12149       {
12150         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12151
12152         DisplayGameControlValues();
12153       }
12154 #else
12155       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12156         DrawGameValue_Time(TimePlayed);
12157 #endif
12158
12159       level.native_em_level->lev->time =
12160         (level.time == 0 ? TimePlayed : TimeLeft);
12161     }
12162
12163     if (tape.recording || tape.playing)
12164       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12165   }
12166
12167 #if 1
12168   UpdateAndDisplayGameControlValues();
12169 #else
12170   UpdateGameDoorValues();
12171   DrawGameDoorValues();
12172 #endif
12173 }
12174
12175 void AdvanceFrameAndPlayerCounters(int player_nr)
12176 {
12177   int i;
12178
12179   /* advance frame counters (global frame counter and time frame counter) */
12180   FrameCounter++;
12181   TimeFrames++;
12182
12183   /* advance player counters (counters for move delay, move animation etc.) */
12184   for (i = 0; i < MAX_PLAYERS; i++)
12185   {
12186     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12187     int move_delay_value = stored_player[i].move_delay_value;
12188     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12189
12190     if (!advance_player_counters)       /* not all players may be affected */
12191       continue;
12192
12193 #if USE_NEW_PLAYER_ANIM
12194     if (move_frames == 0)       /* less than one move per game frame */
12195     {
12196       int stepsize = TILEX / move_delay_value;
12197       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12198       int count = (stored_player[i].is_moving ?
12199                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12200
12201       if (count % delay == 0)
12202         move_frames = 1;
12203     }
12204 #endif
12205
12206     stored_player[i].Frame += move_frames;
12207
12208     if (stored_player[i].MovPos != 0)
12209       stored_player[i].StepFrame += move_frames;
12210
12211     if (stored_player[i].move_delay > 0)
12212       stored_player[i].move_delay--;
12213
12214     /* due to bugs in previous versions, counter must count up, not down */
12215     if (stored_player[i].push_delay != -1)
12216       stored_player[i].push_delay++;
12217
12218     if (stored_player[i].drop_delay > 0)
12219       stored_player[i].drop_delay--;
12220
12221     if (stored_player[i].is_dropping_pressed)
12222       stored_player[i].drop_pressed_delay++;
12223   }
12224 }
12225
12226 void StartGameActions(boolean init_network_game, boolean record_tape,
12227                       long random_seed)
12228 {
12229   unsigned long new_random_seed = InitRND(random_seed);
12230
12231   if (record_tape)
12232     TapeStartRecording(new_random_seed);
12233
12234 #if defined(NETWORK_AVALIABLE)
12235   if (init_network_game)
12236   {
12237     SendToServer_StartPlaying();
12238
12239     return;
12240   }
12241 #endif
12242
12243   InitGame();
12244 }
12245
12246 void GameActions()
12247 {
12248   static unsigned long game_frame_delay = 0;
12249   unsigned long game_frame_delay_value;
12250   byte *recorded_player_action;
12251   byte summarized_player_action = 0;
12252   byte tape_action[MAX_PLAYERS];
12253   int i;
12254
12255   /* detect endless loops, caused by custom element programming */
12256   if (recursion_loop_detected && recursion_loop_depth == 0)
12257   {
12258     char *message = getStringCat3("Internal Error ! Element ",
12259                                   EL_NAME(recursion_loop_element),
12260                                   " caused endless loop ! Quit the game ?");
12261
12262     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12263           EL_NAME(recursion_loop_element));
12264
12265     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12266
12267     recursion_loop_detected = FALSE;    /* if game should be continued */
12268
12269     free(message);
12270
12271     return;
12272   }
12273
12274   if (game.restart_level)
12275     StartGameActions(options.network, setup.autorecord, level.random_seed);
12276
12277   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12278   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12279   {
12280     if (level.native_em_level->lev->home == 0)  /* all players at home */
12281     {
12282       PlayerWins(local_player);
12283
12284       AllPlayersGone = TRUE;
12285
12286       level.native_em_level->lev->home = -1;
12287     }
12288
12289     if (level.native_em_level->ply[0]->alive == 0 &&
12290         level.native_em_level->ply[1]->alive == 0 &&
12291         level.native_em_level->ply[2]->alive == 0 &&
12292         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12293       AllPlayersGone = TRUE;
12294   }
12295   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12296   {
12297     if (game_sp.LevelSolved &&
12298         !game_sp.GameOver)                              /* game won */
12299     {
12300       PlayerWins(local_player);
12301
12302       game_sp.GameOver = TRUE;
12303
12304       AllPlayersGone = TRUE;
12305     }
12306
12307     if (game_sp.GameOver)                               /* game lost */
12308       AllPlayersGone = TRUE;
12309   }
12310
12311   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12312     GameWon();
12313
12314   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12315     TapeStop();
12316
12317   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12318     return;
12319
12320   game_frame_delay_value =
12321     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12322
12323   if (tape.playing && tape.warp_forward && !tape.pausing)
12324     game_frame_delay_value = 0;
12325
12326   /* ---------- main game synchronization point ---------- */
12327
12328   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12329
12330   if (network_playing && !network_player_action_received)
12331   {
12332     /* try to get network player actions in time */
12333
12334 #if defined(NETWORK_AVALIABLE)
12335     /* last chance to get network player actions without main loop delay */
12336     HandleNetworking();
12337 #endif
12338
12339     /* game was quit by network peer */
12340     if (game_status != GAME_MODE_PLAYING)
12341       return;
12342
12343     if (!network_player_action_received)
12344       return;           /* failed to get network player actions in time */
12345
12346     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12347   }
12348
12349   if (tape.pausing)
12350     return;
12351
12352   /* at this point we know that we really continue executing the game */
12353
12354   network_player_action_received = FALSE;
12355
12356   /* when playing tape, read previously recorded player input from tape data */
12357   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12358
12359 #if 1
12360   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12361   if (tape.pausing)
12362     return;
12363 #endif
12364
12365   if (tape.set_centered_player)
12366   {
12367     game.centered_player_nr_next = tape.centered_player_nr_next;
12368     game.set_centered_player = TRUE;
12369   }
12370
12371   for (i = 0; i < MAX_PLAYERS; i++)
12372   {
12373     summarized_player_action |= stored_player[i].action;
12374
12375     if (!network_playing)
12376       stored_player[i].effective_action = stored_player[i].action;
12377   }
12378
12379 #if defined(NETWORK_AVALIABLE)
12380   if (network_playing)
12381     SendToServer_MovePlayer(summarized_player_action);
12382 #endif
12383
12384   if (!options.network && !setup.team_mode)
12385     local_player->effective_action = summarized_player_action;
12386
12387   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12388   {
12389     for (i = 0; i < MAX_PLAYERS; i++)
12390       stored_player[i].effective_action =
12391         (i == game.centered_player_nr ? summarized_player_action : 0);
12392   }
12393
12394   if (recorded_player_action != NULL)
12395     for (i = 0; i < MAX_PLAYERS; i++)
12396       stored_player[i].effective_action = recorded_player_action[i];
12397
12398   for (i = 0; i < MAX_PLAYERS; i++)
12399   {
12400     tape_action[i] = stored_player[i].effective_action;
12401
12402     /* (this can only happen in the R'n'D game engine) */
12403     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12404       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12405   }
12406
12407   /* only record actions from input devices, but not programmed actions */
12408   if (tape.recording)
12409     TapeRecordAction(tape_action);
12410
12411 #if USE_NEW_PLAYER_ASSIGNMENTS
12412   {
12413     byte mapped_action[MAX_PLAYERS];
12414
12415     for (i = 0; i < MAX_PLAYERS; i++)
12416       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12417
12418     for (i = 0; i < MAX_PLAYERS; i++)
12419       stored_player[i].effective_action = mapped_action[i];
12420   }
12421 #endif
12422
12423   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12424   {
12425     GameActions_EM_Main();
12426   }
12427   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12428   {
12429     GameActions_SP_Main();
12430   }
12431   else
12432   {
12433     GameActions_RND();
12434   }
12435 }
12436
12437 void GameActions_EM_Main()
12438 {
12439   byte effective_action[MAX_PLAYERS];
12440   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12441   int i;
12442
12443   for (i = 0; i < MAX_PLAYERS; i++)
12444     effective_action[i] = stored_player[i].effective_action;
12445
12446   GameActions_EM(effective_action, warp_mode);
12447
12448   CheckLevelTime();
12449
12450   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12451 }
12452
12453 void GameActions_SP_Main()
12454 {
12455   byte effective_action[MAX_PLAYERS];
12456   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12457   int i;
12458
12459   for (i = 0; i < MAX_PLAYERS; i++)
12460     effective_action[i] = stored_player[i].effective_action;
12461
12462   GameActions_SP(effective_action, warp_mode);
12463
12464   CheckLevelTime();
12465
12466   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12467 }
12468
12469 void GameActions_RND()
12470 {
12471   int magic_wall_x = 0, magic_wall_y = 0;
12472   int i, x, y, element, graphic;
12473
12474   InitPlayfieldScanModeVars();
12475
12476 #if USE_ONE_MORE_CHANGE_PER_FRAME
12477   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12478   {
12479     SCAN_PLAYFIELD(x, y)
12480     {
12481       ChangeCount[x][y] = 0;
12482       ChangeEvent[x][y] = -1;
12483     }
12484   }
12485 #endif
12486
12487   if (game.set_centered_player)
12488   {
12489     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12490
12491     /* switching to "all players" only possible if all players fit to screen */
12492     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12493     {
12494       game.centered_player_nr_next = game.centered_player_nr;
12495       game.set_centered_player = FALSE;
12496     }
12497
12498     /* do not switch focus to non-existing (or non-active) player */
12499     if (game.centered_player_nr_next >= 0 &&
12500         !stored_player[game.centered_player_nr_next].active)
12501     {
12502       game.centered_player_nr_next = game.centered_player_nr;
12503       game.set_centered_player = FALSE;
12504     }
12505   }
12506
12507   if (game.set_centered_player &&
12508       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12509   {
12510     int sx, sy;
12511
12512     if (game.centered_player_nr_next == -1)
12513     {
12514       setScreenCenteredToAllPlayers(&sx, &sy);
12515     }
12516     else
12517     {
12518       sx = stored_player[game.centered_player_nr_next].jx;
12519       sy = stored_player[game.centered_player_nr_next].jy;
12520     }
12521
12522     game.centered_player_nr = game.centered_player_nr_next;
12523     game.set_centered_player = FALSE;
12524
12525     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12526     DrawGameDoorValues();
12527   }
12528
12529   for (i = 0; i < MAX_PLAYERS; i++)
12530   {
12531     int actual_player_action = stored_player[i].effective_action;
12532
12533 #if 1
12534     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12535        - rnd_equinox_tetrachloride 048
12536        - rnd_equinox_tetrachloride_ii 096
12537        - rnd_emanuel_schmieg 002
12538        - doctor_sloan_ww 001, 020
12539     */
12540     if (stored_player[i].MovPos == 0)
12541       CheckGravityMovement(&stored_player[i]);
12542 #endif
12543
12544     /* overwrite programmed action with tape action */
12545     if (stored_player[i].programmed_action)
12546       actual_player_action = stored_player[i].programmed_action;
12547
12548     PlayerActions(&stored_player[i], actual_player_action);
12549
12550     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12551   }
12552
12553   ScrollScreen(NULL, SCROLL_GO_ON);
12554
12555   /* for backwards compatibility, the following code emulates a fixed bug that
12556      occured when pushing elements (causing elements that just made their last
12557      pushing step to already (if possible) make their first falling step in the
12558      same game frame, which is bad); this code is also needed to use the famous
12559      "spring push bug" which is used in older levels and might be wanted to be
12560      used also in newer levels, but in this case the buggy pushing code is only
12561      affecting the "spring" element and no other elements */
12562
12563   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12564   {
12565     for (i = 0; i < MAX_PLAYERS; i++)
12566     {
12567       struct PlayerInfo *player = &stored_player[i];
12568       int x = player->jx;
12569       int y = player->jy;
12570
12571       if (player->active && player->is_pushing && player->is_moving &&
12572           IS_MOVING(x, y) &&
12573           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12574            Feld[x][y] == EL_SPRING))
12575       {
12576         ContinueMoving(x, y);
12577
12578         /* continue moving after pushing (this is actually a bug) */
12579         if (!IS_MOVING(x, y))
12580           Stop[x][y] = FALSE;
12581       }
12582     }
12583   }
12584
12585 #if 0
12586   debug_print_timestamp(0, "start main loop profiling");
12587 #endif
12588
12589   SCAN_PLAYFIELD(x, y)
12590   {
12591     ChangeCount[x][y] = 0;
12592     ChangeEvent[x][y] = -1;
12593
12594     /* this must be handled before main playfield loop */
12595     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12596     {
12597       MovDelay[x][y]--;
12598       if (MovDelay[x][y] <= 0)
12599         RemoveField(x, y);
12600     }
12601
12602 #if USE_NEW_SNAP_DELAY
12603     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12604     {
12605       MovDelay[x][y]--;
12606       if (MovDelay[x][y] <= 0)
12607       {
12608         RemoveField(x, y);
12609         TEST_DrawLevelField(x, y);
12610
12611         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12612       }
12613     }
12614 #endif
12615
12616 #if DEBUG
12617     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12618     {
12619       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12620       printf("GameActions(): This should never happen!\n");
12621
12622       ChangePage[x][y] = -1;
12623     }
12624 #endif
12625
12626     Stop[x][y] = FALSE;
12627     if (WasJustMoving[x][y] > 0)
12628       WasJustMoving[x][y]--;
12629     if (WasJustFalling[x][y] > 0)
12630       WasJustFalling[x][y]--;
12631     if (CheckCollision[x][y] > 0)
12632       CheckCollision[x][y]--;
12633     if (CheckImpact[x][y] > 0)
12634       CheckImpact[x][y]--;
12635
12636     GfxFrame[x][y]++;
12637
12638     /* reset finished pushing action (not done in ContinueMoving() to allow
12639        continuous pushing animation for elements with zero push delay) */
12640     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12641     {
12642       ResetGfxAnimation(x, y);
12643       TEST_DrawLevelField(x, y);
12644     }
12645
12646 #if DEBUG
12647     if (IS_BLOCKED(x, y))
12648     {
12649       int oldx, oldy;
12650
12651       Blocked2Moving(x, y, &oldx, &oldy);
12652       if (!IS_MOVING(oldx, oldy))
12653       {
12654         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12655         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12656         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12657         printf("GameActions(): This should never happen!\n");
12658       }
12659     }
12660 #endif
12661   }
12662
12663 #if 0
12664   debug_print_timestamp(0, "- time for pre-main loop:");
12665 #endif
12666
12667 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12668   SCAN_PLAYFIELD(x, y)
12669   {
12670     element = Feld[x][y];
12671     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12672
12673 #if 1
12674     {
12675 #if 1
12676       int element2 = element;
12677       int graphic2 = graphic;
12678 #else
12679       int element2 = Feld[x][y];
12680       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12681 #endif
12682       int last_gfx_frame = GfxFrame[x][y];
12683
12684       if (graphic_info[graphic2].anim_global_sync)
12685         GfxFrame[x][y] = FrameCounter;
12686       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12687         GfxFrame[x][y] = CustomValue[x][y];
12688       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12689         GfxFrame[x][y] = element_info[element2].collect_score;
12690       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12691         GfxFrame[x][y] = ChangeDelay[x][y];
12692
12693       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12694         DrawLevelGraphicAnimation(x, y, graphic2);
12695     }
12696 #else
12697     ResetGfxFrame(x, y, TRUE);
12698 #endif
12699
12700 #if 1
12701     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12702         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12703       ResetRandomAnimationValue(x, y);
12704 #endif
12705
12706 #if 1
12707     SetRandomAnimationValue(x, y);
12708 #endif
12709
12710 #if 1
12711     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12712 #endif
12713   }
12714 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12715
12716 #if 0
12717   debug_print_timestamp(0, "- time for TEST loop:     -->");
12718 #endif
12719
12720   SCAN_PLAYFIELD(x, y)
12721   {
12722     element = Feld[x][y];
12723     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12724
12725     ResetGfxFrame(x, y, TRUE);
12726
12727     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12728         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12729       ResetRandomAnimationValue(x, y);
12730
12731     SetRandomAnimationValue(x, y);
12732
12733     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12734
12735     if (IS_INACTIVE(element))
12736     {
12737       if (IS_ANIMATED(graphic))
12738         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12739
12740       continue;
12741     }
12742
12743     /* this may take place after moving, so 'element' may have changed */
12744     if (IS_CHANGING(x, y) &&
12745         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12746     {
12747       int page = element_info[element].event_page_nr[CE_DELAY];
12748
12749 #if 1
12750       HandleElementChange(x, y, page);
12751 #else
12752       if (CAN_CHANGE(element))
12753         HandleElementChange(x, y, page);
12754
12755       if (HAS_ACTION(element))
12756         ExecuteCustomElementAction(x, y, element, page);
12757 #endif
12758
12759       element = Feld[x][y];
12760       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12761     }
12762
12763 #if 0   // ---------------------------------------------------------------------
12764
12765     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12766     {
12767       StartMoving(x, y);
12768
12769       element = Feld[x][y];
12770       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12771
12772       if (IS_ANIMATED(graphic) &&
12773           !IS_MOVING(x, y) &&
12774           !Stop[x][y])
12775         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12776
12777       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12778         TEST_DrawTwinkleOnField(x, y);
12779     }
12780     else if (IS_MOVING(x, y))
12781       ContinueMoving(x, y);
12782     else
12783     {
12784       switch (element)
12785       {
12786         case EL_ACID:
12787         case EL_EXIT_OPEN:
12788         case EL_EM_EXIT_OPEN:
12789         case EL_SP_EXIT_OPEN:
12790         case EL_STEEL_EXIT_OPEN:
12791         case EL_EM_STEEL_EXIT_OPEN:
12792         case EL_SP_TERMINAL:
12793         case EL_SP_TERMINAL_ACTIVE:
12794         case EL_EXTRA_TIME:
12795         case EL_SHIELD_NORMAL:
12796         case EL_SHIELD_DEADLY:
12797           if (IS_ANIMATED(graphic))
12798             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12799           break;
12800
12801         case EL_DYNAMITE_ACTIVE:
12802         case EL_EM_DYNAMITE_ACTIVE:
12803         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12804         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12805         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12806         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12807         case EL_SP_DISK_RED_ACTIVE:
12808           CheckDynamite(x, y);
12809           break;
12810
12811         case EL_AMOEBA_GROWING:
12812           AmoebeWaechst(x, y);
12813           break;
12814
12815         case EL_AMOEBA_SHRINKING:
12816           AmoebaDisappearing(x, y);
12817           break;
12818
12819 #if !USE_NEW_AMOEBA_CODE
12820         case EL_AMOEBA_WET:
12821         case EL_AMOEBA_DRY:
12822         case EL_AMOEBA_FULL:
12823         case EL_BD_AMOEBA:
12824         case EL_EMC_DRIPPER:
12825           AmoebeAbleger(x, y);
12826           break;
12827 #endif
12828
12829         case EL_GAME_OF_LIFE:
12830         case EL_BIOMAZE:
12831           Life(x, y);
12832           break;
12833
12834         case EL_EXIT_CLOSED:
12835           CheckExit(x, y);
12836           break;
12837
12838         case EL_EM_EXIT_CLOSED:
12839           CheckExitEM(x, y);
12840           break;
12841
12842         case EL_STEEL_EXIT_CLOSED:
12843           CheckExitSteel(x, y);
12844           break;
12845
12846         case EL_EM_STEEL_EXIT_CLOSED:
12847           CheckExitSteelEM(x, y);
12848           break;
12849
12850         case EL_SP_EXIT_CLOSED:
12851           CheckExitSP(x, y);
12852           break;
12853
12854         case EL_EXPANDABLE_WALL_GROWING:
12855         case EL_EXPANDABLE_STEELWALL_GROWING:
12856           MauerWaechst(x, y);
12857           break;
12858
12859         case EL_EXPANDABLE_WALL:
12860         case EL_EXPANDABLE_WALL_HORIZONTAL:
12861         case EL_EXPANDABLE_WALL_VERTICAL:
12862         case EL_EXPANDABLE_WALL_ANY:
12863         case EL_BD_EXPANDABLE_WALL:
12864           MauerAbleger(x, y);
12865           break;
12866
12867         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12868         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12869         case EL_EXPANDABLE_STEELWALL_ANY:
12870           MauerAblegerStahl(x, y);
12871           break;
12872
12873         case EL_FLAMES:
12874           CheckForDragon(x, y);
12875           break;
12876
12877         case EL_EXPLOSION:
12878           break;
12879
12880         case EL_ELEMENT_SNAPPING:
12881         case EL_DIAGONAL_SHRINKING:
12882         case EL_DIAGONAL_GROWING:
12883         {
12884           graphic =
12885             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12886
12887           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12888           break;
12889         }
12890
12891         default:
12892           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12893             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12894           break;
12895       }
12896     }
12897
12898 #else   // ---------------------------------------------------------------------
12899
12900     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12901     {
12902       StartMoving(x, y);
12903
12904       element = Feld[x][y];
12905       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12906
12907       if (IS_ANIMATED(graphic) &&
12908           !IS_MOVING(x, y) &&
12909           !Stop[x][y])
12910         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12911
12912       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12913         TEST_DrawTwinkleOnField(x, y);
12914     }
12915     else if ((element == EL_ACID ||
12916               element == EL_EXIT_OPEN ||
12917               element == EL_EM_EXIT_OPEN ||
12918               element == EL_SP_EXIT_OPEN ||
12919               element == EL_STEEL_EXIT_OPEN ||
12920               element == EL_EM_STEEL_EXIT_OPEN ||
12921               element == EL_SP_TERMINAL ||
12922               element == EL_SP_TERMINAL_ACTIVE ||
12923               element == EL_EXTRA_TIME ||
12924               element == EL_SHIELD_NORMAL ||
12925               element == EL_SHIELD_DEADLY) &&
12926              IS_ANIMATED(graphic))
12927       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12928     else if (IS_MOVING(x, y))
12929       ContinueMoving(x, y);
12930     else if (IS_ACTIVE_BOMB(element))
12931       CheckDynamite(x, y);
12932     else if (element == EL_AMOEBA_GROWING)
12933       AmoebeWaechst(x, y);
12934     else if (element == EL_AMOEBA_SHRINKING)
12935       AmoebaDisappearing(x, y);
12936
12937 #if !USE_NEW_AMOEBA_CODE
12938     else if (IS_AMOEBALIVE(element))
12939       AmoebeAbleger(x, y);
12940 #endif
12941
12942     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12943       Life(x, y);
12944     else if (element == EL_EXIT_CLOSED)
12945       CheckExit(x, y);
12946     else if (element == EL_EM_EXIT_CLOSED)
12947       CheckExitEM(x, y);
12948     else if (element == EL_STEEL_EXIT_CLOSED)
12949       CheckExitSteel(x, y);
12950     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12951       CheckExitSteelEM(x, y);
12952     else if (element == EL_SP_EXIT_CLOSED)
12953       CheckExitSP(x, y);
12954     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12955              element == EL_EXPANDABLE_STEELWALL_GROWING)
12956       MauerWaechst(x, y);
12957     else if (element == EL_EXPANDABLE_WALL ||
12958              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12959              element == EL_EXPANDABLE_WALL_VERTICAL ||
12960              element == EL_EXPANDABLE_WALL_ANY ||
12961              element == EL_BD_EXPANDABLE_WALL)
12962       MauerAbleger(x, y);
12963     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12964              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12965              element == EL_EXPANDABLE_STEELWALL_ANY)
12966       MauerAblegerStahl(x, y);
12967     else if (element == EL_FLAMES)
12968       CheckForDragon(x, y);
12969     else if (element == EL_EXPLOSION)
12970       ; /* drawing of correct explosion animation is handled separately */
12971     else if (element == EL_ELEMENT_SNAPPING ||
12972              element == EL_DIAGONAL_SHRINKING ||
12973              element == EL_DIAGONAL_GROWING)
12974     {
12975       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12976
12977       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12978     }
12979     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12981
12982 #endif  // ---------------------------------------------------------------------
12983
12984     if (IS_BELT_ACTIVE(element))
12985       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12986
12987     if (game.magic_wall_active)
12988     {
12989       int jx = local_player->jx, jy = local_player->jy;
12990
12991       /* play the element sound at the position nearest to the player */
12992       if ((element == EL_MAGIC_WALL_FULL ||
12993            element == EL_MAGIC_WALL_ACTIVE ||
12994            element == EL_MAGIC_WALL_EMPTYING ||
12995            element == EL_BD_MAGIC_WALL_FULL ||
12996            element == EL_BD_MAGIC_WALL_ACTIVE ||
12997            element == EL_BD_MAGIC_WALL_EMPTYING ||
12998            element == EL_DC_MAGIC_WALL_FULL ||
12999            element == EL_DC_MAGIC_WALL_ACTIVE ||
13000            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13001           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13002       {
13003         magic_wall_x = x;
13004         magic_wall_y = y;
13005       }
13006     }
13007   }
13008
13009 #if 0
13010   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13011 #endif
13012
13013 #if USE_NEW_AMOEBA_CODE
13014   /* new experimental amoeba growth stuff */
13015   if (!(FrameCounter % 8))
13016   {
13017     static unsigned long random = 1684108901;
13018
13019     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13020     {
13021       x = RND(lev_fieldx);
13022       y = RND(lev_fieldy);
13023       element = Feld[x][y];
13024
13025       if (!IS_PLAYER(x,y) &&
13026           (element == EL_EMPTY ||
13027            CAN_GROW_INTO(element) ||
13028            element == EL_QUICKSAND_EMPTY ||
13029            element == EL_QUICKSAND_FAST_EMPTY ||
13030            element == EL_ACID_SPLASH_LEFT ||
13031            element == EL_ACID_SPLASH_RIGHT))
13032       {
13033         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13034             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13035             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13036             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13037           Feld[x][y] = EL_AMOEBA_DROP;
13038       }
13039
13040       random = random * 129 + 1;
13041     }
13042   }
13043 #endif
13044
13045 #if 0
13046   if (game.explosions_delayed)
13047 #endif
13048   {
13049     game.explosions_delayed = FALSE;
13050
13051     SCAN_PLAYFIELD(x, y)
13052     {
13053       element = Feld[x][y];
13054
13055       if (ExplodeField[x][y])
13056         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13057       else if (element == EL_EXPLOSION)
13058         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13059
13060       ExplodeField[x][y] = EX_TYPE_NONE;
13061     }
13062
13063     game.explosions_delayed = TRUE;
13064   }
13065
13066   if (game.magic_wall_active)
13067   {
13068     if (!(game.magic_wall_time_left % 4))
13069     {
13070       int element = Feld[magic_wall_x][magic_wall_y];
13071
13072       if (element == EL_BD_MAGIC_WALL_FULL ||
13073           element == EL_BD_MAGIC_WALL_ACTIVE ||
13074           element == EL_BD_MAGIC_WALL_EMPTYING)
13075         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13076       else if (element == EL_DC_MAGIC_WALL_FULL ||
13077                element == EL_DC_MAGIC_WALL_ACTIVE ||
13078                element == EL_DC_MAGIC_WALL_EMPTYING)
13079         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13080       else
13081         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13082     }
13083
13084     if (game.magic_wall_time_left > 0)
13085     {
13086       game.magic_wall_time_left--;
13087
13088       if (!game.magic_wall_time_left)
13089       {
13090         SCAN_PLAYFIELD(x, y)
13091         {
13092           element = Feld[x][y];
13093
13094           if (element == EL_MAGIC_WALL_ACTIVE ||
13095               element == EL_MAGIC_WALL_FULL)
13096           {
13097             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13098             TEST_DrawLevelField(x, y);
13099           }
13100           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13101                    element == EL_BD_MAGIC_WALL_FULL)
13102           {
13103             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13104             TEST_DrawLevelField(x, y);
13105           }
13106           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13107                    element == EL_DC_MAGIC_WALL_FULL)
13108           {
13109             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13110             TEST_DrawLevelField(x, y);
13111           }
13112         }
13113
13114         game.magic_wall_active = FALSE;
13115       }
13116     }
13117   }
13118
13119   if (game.light_time_left > 0)
13120   {
13121     game.light_time_left--;
13122
13123     if (game.light_time_left == 0)
13124       RedrawAllLightSwitchesAndInvisibleElements();
13125   }
13126
13127   if (game.timegate_time_left > 0)
13128   {
13129     game.timegate_time_left--;
13130
13131     if (game.timegate_time_left == 0)
13132       CloseAllOpenTimegates();
13133   }
13134
13135   if (game.lenses_time_left > 0)
13136   {
13137     game.lenses_time_left--;
13138
13139     if (game.lenses_time_left == 0)
13140       RedrawAllInvisibleElementsForLenses();
13141   }
13142
13143   if (game.magnify_time_left > 0)
13144   {
13145     game.magnify_time_left--;
13146
13147     if (game.magnify_time_left == 0)
13148       RedrawAllInvisibleElementsForMagnifier();
13149   }
13150
13151   for (i = 0; i < MAX_PLAYERS; i++)
13152   {
13153     struct PlayerInfo *player = &stored_player[i];
13154
13155     if (SHIELD_ON(player))
13156     {
13157       if (player->shield_deadly_time_left)
13158         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13159       else if (player->shield_normal_time_left)
13160         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13161     }
13162   }
13163
13164 #if USE_DELAYED_GFX_REDRAW
13165   SCAN_PLAYFIELD(x, y)
13166   {
13167 #if 1
13168     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13169 #else
13170     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13171         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13172 #endif
13173     {
13174       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13175          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13176
13177       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13178         DrawLevelField(x, y);
13179
13180       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13181         DrawLevelFieldCrumbled(x, y);
13182
13183       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13184         DrawLevelFieldCrumbledNeighbours(x, y);
13185
13186       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13187         DrawTwinkleOnField(x, y);
13188     }
13189
13190     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13191   }
13192 #endif
13193
13194   CheckLevelTime();
13195
13196   DrawAllPlayers();
13197   PlayAllPlayersSound();
13198
13199   if (options.debug)                    /* calculate frames per second */
13200   {
13201     static unsigned long fps_counter = 0;
13202     static int fps_frames = 0;
13203     unsigned long fps_delay_ms = Counter() - fps_counter;
13204
13205     fps_frames++;
13206
13207     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13208     {
13209       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13210
13211       fps_frames = 0;
13212       fps_counter = Counter();
13213     }
13214
13215     redraw_mask |= REDRAW_FPS;
13216   }
13217
13218   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13219
13220   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13221   {
13222     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13223
13224     local_player->show_envelope = 0;
13225   }
13226
13227 #if 0
13228   debug_print_timestamp(0, "stop main loop profiling ");
13229   printf("----------------------------------------------------------\n");
13230 #endif
13231
13232   /* use random number generator in every frame to make it less predictable */
13233   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13234     RND(1);
13235 }
13236
13237 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13238 {
13239   int min_x = x, min_y = y, max_x = x, max_y = y;
13240   int i;
13241
13242   for (i = 0; i < MAX_PLAYERS; i++)
13243   {
13244     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13245
13246     if (!stored_player[i].active || &stored_player[i] == player)
13247       continue;
13248
13249     min_x = MIN(min_x, jx);
13250     min_y = MIN(min_y, jy);
13251     max_x = MAX(max_x, jx);
13252     max_y = MAX(max_y, jy);
13253   }
13254
13255   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13256 }
13257
13258 static boolean AllPlayersInVisibleScreen()
13259 {
13260   int i;
13261
13262   for (i = 0; i < MAX_PLAYERS; i++)
13263   {
13264     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13265
13266     if (!stored_player[i].active)
13267       continue;
13268
13269     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13270       return FALSE;
13271   }
13272
13273   return TRUE;
13274 }
13275
13276 void ScrollLevel(int dx, int dy)
13277 {
13278 #if 0
13279   /* (directly solved in BlitBitmap() now) */
13280   static Bitmap *bitmap_db_field2 = NULL;
13281   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13282   int x, y;
13283 #else
13284   int x, y;
13285 #endif
13286
13287 #if 0
13288   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13289   /* only horizontal XOR vertical scroll direction allowed */
13290   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13291     return;
13292 #endif
13293
13294 #if 0
13295   /* (directly solved in BlitBitmap() now) */
13296   if (bitmap_db_field2 == NULL)
13297     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13298
13299   /* needed when blitting directly to same bitmap -- should not be needed with
13300      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13301   BlitBitmap(drawto_field, bitmap_db_field2,
13302              FX + TILEX * (dx == -1) - softscroll_offset,
13303              FY + TILEY * (dy == -1) - softscroll_offset,
13304              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13305              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13306              FX + TILEX * (dx == 1) - softscroll_offset,
13307              FY + TILEY * (dy == 1) - softscroll_offset);
13308   BlitBitmap(bitmap_db_field2, drawto_field,
13309              FX + TILEX * (dx == 1) - softscroll_offset,
13310              FY + TILEY * (dy == 1) - softscroll_offset,
13311              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13312              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13313              FX + TILEX * (dx == 1) - softscroll_offset,
13314              FY + TILEY * (dy == 1) - softscroll_offset);
13315
13316 #else
13317
13318 #if 0
13319   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13320   int xsize = (BX2 - BX1 + 1);
13321   int ysize = (BY2 - BY1 + 1);
13322   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13323   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13324   int step  = (start < end ? +1 : -1);
13325
13326   for (i = start; i != end; i += step)
13327   {
13328     BlitBitmap(drawto_field, drawto_field,
13329                FX + TILEX * (dx != 0 ? i + step : 0),
13330                FY + TILEY * (dy != 0 ? i + step : 0),
13331                TILEX * (dx != 0 ? 1 : xsize),
13332                TILEY * (dy != 0 ? 1 : ysize),
13333                FX + TILEX * (dx != 0 ? i : 0),
13334                FY + TILEY * (dy != 0 ? i : 0));
13335   }
13336
13337 #else
13338
13339   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13340
13341   BlitBitmap(drawto_field, drawto_field,
13342              FX + TILEX * (dx == -1) - softscroll_offset,
13343              FY + TILEY * (dy == -1) - softscroll_offset,
13344              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13345              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13346              FX + TILEX * (dx == 1) - softscroll_offset,
13347              FY + TILEY * (dy == 1) - softscroll_offset);
13348 #endif
13349 #endif
13350
13351   if (dx != 0)
13352   {
13353     x = (dx == 1 ? BX1 : BX2);
13354     for (y = BY1; y <= BY2; y++)
13355       DrawScreenField(x, y);
13356   }
13357
13358   if (dy != 0)
13359   {
13360     y = (dy == 1 ? BY1 : BY2);
13361     for (x = BX1; x <= BX2; x++)
13362       DrawScreenField(x, y);
13363   }
13364
13365   redraw_mask |= REDRAW_FIELD;
13366 }
13367
13368 static boolean canFallDown(struct PlayerInfo *player)
13369 {
13370   int jx = player->jx, jy = player->jy;
13371
13372   return (IN_LEV_FIELD(jx, jy + 1) &&
13373           (IS_FREE(jx, jy + 1) ||
13374            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13375           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13376           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13377 }
13378
13379 static boolean canPassField(int x, int y, int move_dir)
13380 {
13381   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13382   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13383   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13384   int nextx = x + dx;
13385   int nexty = y + dy;
13386   int element = Feld[x][y];
13387
13388   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13389           !CAN_MOVE(element) &&
13390           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13391           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13392           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13393 }
13394
13395 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13396 {
13397   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13398   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13399   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13400   int newx = x + dx;
13401   int newy = y + dy;
13402
13403   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13404           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13405           (IS_DIGGABLE(Feld[newx][newy]) ||
13406            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13407            canPassField(newx, newy, move_dir)));
13408 }
13409
13410 static void CheckGravityMovement(struct PlayerInfo *player)
13411 {
13412 #if USE_PLAYER_GRAVITY
13413   if (player->gravity && !player->programmed_action)
13414 #else
13415   if (game.gravity && !player->programmed_action)
13416 #endif
13417   {
13418     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13419     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13420     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13421     int jx = player->jx, jy = player->jy;
13422     boolean player_is_moving_to_valid_field =
13423       (!player_is_snapping &&
13424        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13425         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13426     boolean player_can_fall_down = canFallDown(player);
13427
13428     if (player_can_fall_down &&
13429         !player_is_moving_to_valid_field)
13430       player->programmed_action = MV_DOWN;
13431   }
13432 }
13433
13434 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13435 {
13436   return CheckGravityMovement(player);
13437
13438 #if USE_PLAYER_GRAVITY
13439   if (player->gravity && !player->programmed_action)
13440 #else
13441   if (game.gravity && !player->programmed_action)
13442 #endif
13443   {
13444     int jx = player->jx, jy = player->jy;
13445     boolean field_under_player_is_free =
13446       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13447     boolean player_is_standing_on_valid_field =
13448       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13449        (IS_WALKABLE(Feld[jx][jy]) &&
13450         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13451
13452     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13453       player->programmed_action = MV_DOWN;
13454   }
13455 }
13456
13457 /*
13458   MovePlayerOneStep()
13459   -----------------------------------------------------------------------------
13460   dx, dy:               direction (non-diagonal) to try to move the player to
13461   real_dx, real_dy:     direction as read from input device (can be diagonal)
13462 */
13463
13464 boolean MovePlayerOneStep(struct PlayerInfo *player,
13465                           int dx, int dy, int real_dx, int real_dy)
13466 {
13467   int jx = player->jx, jy = player->jy;
13468   int new_jx = jx + dx, new_jy = jy + dy;
13469 #if !USE_FIXED_DONT_RUN_INTO
13470   int element;
13471 #endif
13472   int can_move;
13473   boolean player_can_move = !player->cannot_move;
13474
13475   if (!player->active || (!dx && !dy))
13476     return MP_NO_ACTION;
13477
13478   player->MovDir = (dx < 0 ? MV_LEFT :
13479                     dx > 0 ? MV_RIGHT :
13480                     dy < 0 ? MV_UP :
13481                     dy > 0 ? MV_DOWN :  MV_NONE);
13482
13483   if (!IN_LEV_FIELD(new_jx, new_jy))
13484     return MP_NO_ACTION;
13485
13486   if (!player_can_move)
13487   {
13488     if (player->MovPos == 0)
13489     {
13490       player->is_moving = FALSE;
13491       player->is_digging = FALSE;
13492       player->is_collecting = FALSE;
13493       player->is_snapping = FALSE;
13494       player->is_pushing = FALSE;
13495     }
13496   }
13497
13498 #if 1
13499   if (!options.network && game.centered_player_nr == -1 &&
13500       !AllPlayersInSight(player, new_jx, new_jy))
13501     return MP_NO_ACTION;
13502 #else
13503   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13504     return MP_NO_ACTION;
13505 #endif
13506
13507 #if !USE_FIXED_DONT_RUN_INTO
13508   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13509
13510   /* (moved to DigField()) */
13511   if (player_can_move && DONT_RUN_INTO(element))
13512   {
13513     if (element == EL_ACID && dx == 0 && dy == 1)
13514     {
13515       SplashAcid(new_jx, new_jy);
13516       Feld[jx][jy] = EL_PLAYER_1;
13517       InitMovingField(jx, jy, MV_DOWN);
13518       Store[jx][jy] = EL_ACID;
13519       ContinueMoving(jx, jy);
13520       BuryPlayer(player);
13521     }
13522     else
13523       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13524
13525     return MP_MOVING;
13526   }
13527 #endif
13528
13529   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13530   if (can_move != MP_MOVING)
13531     return can_move;
13532
13533   /* check if DigField() has caused relocation of the player */
13534   if (player->jx != jx || player->jy != jy)
13535     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13536
13537   StorePlayer[jx][jy] = 0;
13538   player->last_jx = jx;
13539   player->last_jy = jy;
13540   player->jx = new_jx;
13541   player->jy = new_jy;
13542   StorePlayer[new_jx][new_jy] = player->element_nr;
13543
13544   if (player->move_delay_value_next != -1)
13545   {
13546     player->move_delay_value = player->move_delay_value_next;
13547     player->move_delay_value_next = -1;
13548   }
13549
13550   player->MovPos =
13551     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13552
13553   player->step_counter++;
13554
13555   PlayerVisit[jx][jy] = FrameCounter;
13556
13557 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13558   player->is_moving = TRUE;
13559 #endif
13560
13561 #if 1
13562   /* should better be called in MovePlayer(), but this breaks some tapes */
13563   ScrollPlayer(player, SCROLL_INIT);
13564 #endif
13565
13566   return MP_MOVING;
13567 }
13568
13569 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13570 {
13571   int jx = player->jx, jy = player->jy;
13572   int old_jx = jx, old_jy = jy;
13573   int moved = MP_NO_ACTION;
13574
13575   if (!player->active)
13576     return FALSE;
13577
13578   if (!dx && !dy)
13579   {
13580     if (player->MovPos == 0)
13581     {
13582       player->is_moving = FALSE;
13583       player->is_digging = FALSE;
13584       player->is_collecting = FALSE;
13585       player->is_snapping = FALSE;
13586       player->is_pushing = FALSE;
13587     }
13588
13589     return FALSE;
13590   }
13591
13592   if (player->move_delay > 0)
13593     return FALSE;
13594
13595   player->move_delay = -1;              /* set to "uninitialized" value */
13596
13597   /* store if player is automatically moved to next field */
13598   player->is_auto_moving = (player->programmed_action != MV_NONE);
13599
13600   /* remove the last programmed player action */
13601   player->programmed_action = 0;
13602
13603   if (player->MovPos)
13604   {
13605     /* should only happen if pre-1.2 tape recordings are played */
13606     /* this is only for backward compatibility */
13607
13608     int original_move_delay_value = player->move_delay_value;
13609
13610 #if DEBUG
13611     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13612            tape.counter);
13613 #endif
13614
13615     /* scroll remaining steps with finest movement resolution */
13616     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13617
13618     while (player->MovPos)
13619     {
13620       ScrollPlayer(player, SCROLL_GO_ON);
13621       ScrollScreen(NULL, SCROLL_GO_ON);
13622
13623       AdvanceFrameAndPlayerCounters(player->index_nr);
13624
13625       DrawAllPlayers();
13626       BackToFront();
13627     }
13628
13629     player->move_delay_value = original_move_delay_value;
13630   }
13631
13632   player->is_active = FALSE;
13633
13634   if (player->last_move_dir & MV_HORIZONTAL)
13635   {
13636     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13637       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13638   }
13639   else
13640   {
13641     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13642       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13643   }
13644
13645 #if USE_FIXED_BORDER_RUNNING_GFX
13646   if (!moved && !player->is_active)
13647   {
13648     player->is_moving = FALSE;
13649     player->is_digging = FALSE;
13650     player->is_collecting = FALSE;
13651     player->is_snapping = FALSE;
13652     player->is_pushing = FALSE;
13653   }
13654 #endif
13655
13656   jx = player->jx;
13657   jy = player->jy;
13658
13659 #if 1
13660   if (moved & MP_MOVING && !ScreenMovPos &&
13661       (player->index_nr == game.centered_player_nr ||
13662        game.centered_player_nr == -1))
13663 #else
13664   if (moved & MP_MOVING && !ScreenMovPos &&
13665       (player == local_player || !options.network))
13666 #endif
13667   {
13668     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13669     int offset = game.scroll_delay_value;
13670
13671     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13672     {
13673       /* actual player has left the screen -- scroll in that direction */
13674       if (jx != old_jx)         /* player has moved horizontally */
13675         scroll_x += (jx - old_jx);
13676       else                      /* player has moved vertically */
13677         scroll_y += (jy - old_jy);
13678     }
13679     else
13680     {
13681       if (jx != old_jx)         /* player has moved horizontally */
13682       {
13683         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13684             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13685           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13686
13687         /* don't scroll over playfield boundaries */
13688         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13689           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13690
13691         /* don't scroll more than one field at a time */
13692         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13693
13694         /* don't scroll against the player's moving direction */
13695         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13696             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13697           scroll_x = old_scroll_x;
13698       }
13699       else                      /* player has moved vertically */
13700       {
13701         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13702             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13703           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13704
13705         /* don't scroll over playfield boundaries */
13706         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13707           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13708
13709         /* don't scroll more than one field at a time */
13710         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13711
13712         /* don't scroll against the player's moving direction */
13713         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13714             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13715           scroll_y = old_scroll_y;
13716       }
13717     }
13718
13719     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13720     {
13721 #if 1
13722       if (!options.network && game.centered_player_nr == -1 &&
13723           !AllPlayersInVisibleScreen())
13724       {
13725         scroll_x = old_scroll_x;
13726         scroll_y = old_scroll_y;
13727       }
13728       else
13729 #else
13730       if (!options.network && !AllPlayersInVisibleScreen())
13731       {
13732         scroll_x = old_scroll_x;
13733         scroll_y = old_scroll_y;
13734       }
13735       else
13736 #endif
13737       {
13738         ScrollScreen(player, SCROLL_INIT);
13739         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13740       }
13741     }
13742   }
13743
13744   player->StepFrame = 0;
13745
13746   if (moved & MP_MOVING)
13747   {
13748     if (old_jx != jx && old_jy == jy)
13749       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13750     else if (old_jx == jx && old_jy != jy)
13751       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13752
13753     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13754
13755     player->last_move_dir = player->MovDir;
13756     player->is_moving = TRUE;
13757     player->is_snapping = FALSE;
13758     player->is_switching = FALSE;
13759     player->is_dropping = FALSE;
13760     player->is_dropping_pressed = FALSE;
13761     player->drop_pressed_delay = 0;
13762
13763 #if 0
13764     /* should better be called here than above, but this breaks some tapes */
13765     ScrollPlayer(player, SCROLL_INIT);
13766 #endif
13767   }
13768   else
13769   {
13770     CheckGravityMovementWhenNotMoving(player);
13771
13772     player->is_moving = FALSE;
13773
13774     /* at this point, the player is allowed to move, but cannot move right now
13775        (e.g. because of something blocking the way) -- ensure that the player
13776        is also allowed to move in the next frame (in old versions before 3.1.1,
13777        the player was forced to wait again for eight frames before next try) */
13778
13779     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13780       player->move_delay = 0;   /* allow direct movement in the next frame */
13781   }
13782
13783   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13784     player->move_delay = player->move_delay_value;
13785
13786   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13787   {
13788     TestIfPlayerTouchesBadThing(jx, jy);
13789     TestIfPlayerTouchesCustomElement(jx, jy);
13790   }
13791
13792   if (!player->active)
13793     RemovePlayer(player);
13794
13795   return moved;
13796 }
13797
13798 void ScrollPlayer(struct PlayerInfo *player, int mode)
13799 {
13800   int jx = player->jx, jy = player->jy;
13801   int last_jx = player->last_jx, last_jy = player->last_jy;
13802   int move_stepsize = TILEX / player->move_delay_value;
13803
13804 #if USE_NEW_PLAYER_SPEED
13805   if (!player->active)
13806     return;
13807
13808   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13809     return;
13810 #else
13811   if (!player->active || player->MovPos == 0)
13812     return;
13813 #endif
13814
13815   if (mode == SCROLL_INIT)
13816   {
13817     player->actual_frame_counter = FrameCounter;
13818     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13819
13820     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13821         Feld[last_jx][last_jy] == EL_EMPTY)
13822     {
13823       int last_field_block_delay = 0;   /* start with no blocking at all */
13824       int block_delay_adjustment = player->block_delay_adjustment;
13825
13826       /* if player blocks last field, add delay for exactly one move */
13827       if (player->block_last_field)
13828       {
13829         last_field_block_delay += player->move_delay_value;
13830
13831         /* when blocking enabled, prevent moving up despite gravity */
13832 #if USE_PLAYER_GRAVITY
13833         if (player->gravity && player->MovDir == MV_UP)
13834           block_delay_adjustment = -1;
13835 #else
13836         if (game.gravity && player->MovDir == MV_UP)
13837           block_delay_adjustment = -1;
13838 #endif
13839       }
13840
13841       /* add block delay adjustment (also possible when not blocking) */
13842       last_field_block_delay += block_delay_adjustment;
13843
13844       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13845       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13846     }
13847
13848 #if USE_NEW_PLAYER_SPEED
13849     if (player->MovPos != 0)    /* player has not yet reached destination */
13850       return;
13851 #else
13852     return;
13853 #endif
13854   }
13855   else if (!FrameReached(&player->actual_frame_counter, 1))
13856     return;
13857
13858 #if USE_NEW_PLAYER_SPEED
13859   if (player->MovPos != 0)
13860   {
13861     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13862     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13863
13864     /* before DrawPlayer() to draw correct player graphic for this case */
13865     if (player->MovPos == 0)
13866       CheckGravityMovement(player);
13867   }
13868 #else
13869   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13870   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13871
13872   /* before DrawPlayer() to draw correct player graphic for this case */
13873   if (player->MovPos == 0)
13874     CheckGravityMovement(player);
13875 #endif
13876
13877   if (player->MovPos == 0)      /* player reached destination field */
13878   {
13879     if (player->move_delay_reset_counter > 0)
13880     {
13881       player->move_delay_reset_counter--;
13882
13883       if (player->move_delay_reset_counter == 0)
13884       {
13885         /* continue with normal speed after quickly moving through gate */
13886         HALVE_PLAYER_SPEED(player);
13887
13888         /* be able to make the next move without delay */
13889         player->move_delay = 0;
13890       }
13891     }
13892
13893     player->last_jx = jx;
13894     player->last_jy = jy;
13895
13896     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13897         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13898 #if 1
13899         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13900 #endif
13901         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13902         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13903 #if 1
13904         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13905 #endif
13906         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13907         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13908     {
13909       DrawPlayer(player);       /* needed here only to cleanup last field */
13910       RemovePlayer(player);
13911
13912       if (local_player->friends_still_needed == 0 ||
13913           IS_SP_ELEMENT(Feld[jx][jy]))
13914         PlayerWins(player);
13915     }
13916
13917     /* this breaks one level: "machine", level 000 */
13918     {
13919       int move_direction = player->MovDir;
13920       int enter_side = MV_DIR_OPPOSITE(move_direction);
13921       int leave_side = move_direction;
13922       int old_jx = last_jx;
13923       int old_jy = last_jy;
13924       int old_element = Feld[old_jx][old_jy];
13925       int new_element = Feld[jx][jy];
13926
13927       if (IS_CUSTOM_ELEMENT(old_element))
13928         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13929                                    CE_LEFT_BY_PLAYER,
13930                                    player->index_bit, leave_side);
13931
13932       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13933                                           CE_PLAYER_LEAVES_X,
13934                                           player->index_bit, leave_side);
13935
13936       if (IS_CUSTOM_ELEMENT(new_element))
13937         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13938                                    player->index_bit, enter_side);
13939
13940       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13941                                           CE_PLAYER_ENTERS_X,
13942                                           player->index_bit, enter_side);
13943
13944 #if USE_FIX_CE_ACTION_WITH_PLAYER
13945       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13946                                         CE_MOVE_OF_X, move_direction);
13947 #else
13948       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13949                                         CE_MOVE_OF_X, move_direction);
13950 #endif
13951     }
13952
13953     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13954     {
13955       TestIfPlayerTouchesBadThing(jx, jy);
13956       TestIfPlayerTouchesCustomElement(jx, jy);
13957
13958       /* needed because pushed element has not yet reached its destination,
13959          so it would trigger a change event at its previous field location */
13960       if (!player->is_pushing)
13961         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13962
13963       if (!player->active)
13964         RemovePlayer(player);
13965     }
13966
13967     if (!local_player->LevelSolved && level.use_step_counter)
13968     {
13969       int i;
13970
13971       TimePlayed++;
13972
13973       if (TimeLeft > 0)
13974       {
13975         TimeLeft--;
13976
13977         if (TimeLeft <= 10 && setup.time_limit)
13978           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13979
13980 #if 1
13981         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13982
13983         DisplayGameControlValues();
13984 #else
13985         DrawGameValue_Time(TimeLeft);
13986 #endif
13987
13988         if (!TimeLeft && setup.time_limit)
13989           for (i = 0; i < MAX_PLAYERS; i++)
13990             KillPlayer(&stored_player[i]);
13991       }
13992 #if 1
13993       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13994       {
13995         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13996
13997         DisplayGameControlValues();
13998       }
13999 #else
14000       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14001         DrawGameValue_Time(TimePlayed);
14002 #endif
14003     }
14004
14005     if (tape.single_step && tape.recording && !tape.pausing &&
14006         !player->programmed_action)
14007       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14008   }
14009 }
14010
14011 void ScrollScreen(struct PlayerInfo *player, int mode)
14012 {
14013   static unsigned long screen_frame_counter = 0;
14014
14015   if (mode == SCROLL_INIT)
14016   {
14017     /* set scrolling step size according to actual player's moving speed */
14018     ScrollStepSize = TILEX / player->move_delay_value;
14019
14020     screen_frame_counter = FrameCounter;
14021     ScreenMovDir = player->MovDir;
14022     ScreenMovPos = player->MovPos;
14023     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14024     return;
14025   }
14026   else if (!FrameReached(&screen_frame_counter, 1))
14027     return;
14028
14029   if (ScreenMovPos)
14030   {
14031     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14032     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14033     redraw_mask |= REDRAW_FIELD;
14034   }
14035   else
14036     ScreenMovDir = MV_NONE;
14037 }
14038
14039 void TestIfPlayerTouchesCustomElement(int x, int y)
14040 {
14041   static int xy[4][2] =
14042   {
14043     { 0, -1 },
14044     { -1, 0 },
14045     { +1, 0 },
14046     { 0, +1 }
14047   };
14048   static int trigger_sides[4][2] =
14049   {
14050     /* center side       border side */
14051     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14052     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14053     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14054     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14055   };
14056   static int touch_dir[4] =
14057   {
14058     MV_LEFT | MV_RIGHT,
14059     MV_UP   | MV_DOWN,
14060     MV_UP   | MV_DOWN,
14061     MV_LEFT | MV_RIGHT
14062   };
14063   int center_element = Feld[x][y];      /* should always be non-moving! */
14064   int i;
14065
14066   for (i = 0; i < NUM_DIRECTIONS; i++)
14067   {
14068     int xx = x + xy[i][0];
14069     int yy = y + xy[i][1];
14070     int center_side = trigger_sides[i][0];
14071     int border_side = trigger_sides[i][1];
14072     int border_element;
14073
14074     if (!IN_LEV_FIELD(xx, yy))
14075       continue;
14076
14077     if (IS_PLAYER(x, y))                /* player found at center element */
14078     {
14079       struct PlayerInfo *player = PLAYERINFO(x, y);
14080
14081       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14082         border_element = Feld[xx][yy];          /* may be moving! */
14083       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14084         border_element = Feld[xx][yy];
14085       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14086         border_element = MovingOrBlocked2Element(xx, yy);
14087       else
14088         continue;               /* center and border element do not touch */
14089
14090       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14091                                  player->index_bit, border_side);
14092       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14093                                           CE_PLAYER_TOUCHES_X,
14094                                           player->index_bit, border_side);
14095
14096 #if USE_FIX_CE_ACTION_WITH_PLAYER
14097       {
14098         /* use player element that is initially defined in the level playfield,
14099            not the player element that corresponds to the runtime player number
14100            (example: a level that contains EL_PLAYER_3 as the only player would
14101            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14102         int player_element = PLAYERINFO(x, y)->initial_element;
14103
14104         CheckElementChangeBySide(xx, yy, border_element, player_element,
14105                                  CE_TOUCHING_X, border_side);
14106       }
14107 #endif
14108     }
14109     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14110     {
14111       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14112
14113       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14114       {
14115         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14116           continue;             /* center and border element do not touch */
14117       }
14118
14119       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14120                                  player->index_bit, center_side);
14121       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14122                                           CE_PLAYER_TOUCHES_X,
14123                                           player->index_bit, center_side);
14124
14125 #if USE_FIX_CE_ACTION_WITH_PLAYER
14126       {
14127         /* use player element that is initially defined in the level playfield,
14128            not the player element that corresponds to the runtime player number
14129            (example: a level that contains EL_PLAYER_3 as the only player would
14130            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131         int player_element = PLAYERINFO(xx, yy)->initial_element;
14132
14133         CheckElementChangeBySide(x, y, center_element, player_element,
14134                                  CE_TOUCHING_X, center_side);
14135       }
14136 #endif
14137
14138       break;
14139     }
14140   }
14141 }
14142
14143 #if USE_ELEMENT_TOUCHING_BUGFIX
14144
14145 void TestIfElementTouchesCustomElement(int x, int y)
14146 {
14147   static int xy[4][2] =
14148   {
14149     { 0, -1 },
14150     { -1, 0 },
14151     { +1, 0 },
14152     { 0, +1 }
14153   };
14154   static int trigger_sides[4][2] =
14155   {
14156     /* center side      border side */
14157     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14158     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14159     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14160     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14161   };
14162   static int touch_dir[4] =
14163   {
14164     MV_LEFT | MV_RIGHT,
14165     MV_UP   | MV_DOWN,
14166     MV_UP   | MV_DOWN,
14167     MV_LEFT | MV_RIGHT
14168   };
14169   boolean change_center_element = FALSE;
14170   int center_element = Feld[x][y];      /* should always be non-moving! */
14171   int border_element_old[NUM_DIRECTIONS];
14172   int i;
14173
14174   for (i = 0; i < NUM_DIRECTIONS; i++)
14175   {
14176     int xx = x + xy[i][0];
14177     int yy = y + xy[i][1];
14178     int border_element;
14179
14180     border_element_old[i] = -1;
14181
14182     if (!IN_LEV_FIELD(xx, yy))
14183       continue;
14184
14185     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14186       border_element = Feld[xx][yy];    /* may be moving! */
14187     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14188       border_element = Feld[xx][yy];
14189     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14190       border_element = MovingOrBlocked2Element(xx, yy);
14191     else
14192       continue;                 /* center and border element do not touch */
14193
14194     border_element_old[i] = border_element;
14195   }
14196
14197   for (i = 0; i < NUM_DIRECTIONS; i++)
14198   {
14199     int xx = x + xy[i][0];
14200     int yy = y + xy[i][1];
14201     int center_side = trigger_sides[i][0];
14202     int border_element = border_element_old[i];
14203
14204     if (border_element == -1)
14205       continue;
14206
14207     /* check for change of border element */
14208     CheckElementChangeBySide(xx, yy, border_element, center_element,
14209                              CE_TOUCHING_X, center_side);
14210
14211     /* (center element cannot be player, so we dont have to check this here) */
14212   }
14213
14214   for (i = 0; i < NUM_DIRECTIONS; i++)
14215   {
14216     int xx = x + xy[i][0];
14217     int yy = y + xy[i][1];
14218     int border_side = trigger_sides[i][1];
14219     int border_element = border_element_old[i];
14220
14221     if (border_element == -1)
14222       continue;
14223
14224     /* check for change of center element (but change it only once) */
14225     if (!change_center_element)
14226       change_center_element =
14227         CheckElementChangeBySide(x, y, center_element, border_element,
14228                                  CE_TOUCHING_X, border_side);
14229
14230 #if USE_FIX_CE_ACTION_WITH_PLAYER
14231     if (IS_PLAYER(xx, yy))
14232     {
14233       /* use player element that is initially defined in the level playfield,
14234          not the player element that corresponds to the runtime player number
14235          (example: a level that contains EL_PLAYER_3 as the only player would
14236          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14237       int player_element = PLAYERINFO(xx, yy)->initial_element;
14238
14239       CheckElementChangeBySide(x, y, center_element, player_element,
14240                                CE_TOUCHING_X, border_side);
14241     }
14242 #endif
14243   }
14244 }
14245
14246 #else
14247
14248 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14249 {
14250   static int xy[4][2] =
14251   {
14252     { 0, -1 },
14253     { -1, 0 },
14254     { +1, 0 },
14255     { 0, +1 }
14256   };
14257   static int trigger_sides[4][2] =
14258   {
14259     /* center side      border side */
14260     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14261     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14262     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14263     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14264   };
14265   static int touch_dir[4] =
14266   {
14267     MV_LEFT | MV_RIGHT,
14268     MV_UP   | MV_DOWN,
14269     MV_UP   | MV_DOWN,
14270     MV_LEFT | MV_RIGHT
14271   };
14272   boolean change_center_element = FALSE;
14273   int center_element = Feld[x][y];      /* should always be non-moving! */
14274   int i;
14275
14276   for (i = 0; i < NUM_DIRECTIONS; i++)
14277   {
14278     int xx = x + xy[i][0];
14279     int yy = y + xy[i][1];
14280     int center_side = trigger_sides[i][0];
14281     int border_side = trigger_sides[i][1];
14282     int border_element;
14283
14284     if (!IN_LEV_FIELD(xx, yy))
14285       continue;
14286
14287     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14288       border_element = Feld[xx][yy];    /* may be moving! */
14289     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14290       border_element = Feld[xx][yy];
14291     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14292       border_element = MovingOrBlocked2Element(xx, yy);
14293     else
14294       continue;                 /* center and border element do not touch */
14295
14296     /* check for change of center element (but change it only once) */
14297     if (!change_center_element)
14298       change_center_element =
14299         CheckElementChangeBySide(x, y, center_element, border_element,
14300                                  CE_TOUCHING_X, border_side);
14301
14302     /* check for change of border element */
14303     CheckElementChangeBySide(xx, yy, border_element, center_element,
14304                              CE_TOUCHING_X, center_side);
14305   }
14306 }
14307
14308 #endif
14309
14310 void TestIfElementHitsCustomElement(int x, int y, int direction)
14311 {
14312   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14313   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14314   int hitx = x + dx, hity = y + dy;
14315   int hitting_element = Feld[x][y];
14316   int touched_element;
14317
14318   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14319     return;
14320
14321   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14322                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14323
14324   if (IN_LEV_FIELD(hitx, hity))
14325   {
14326     int opposite_direction = MV_DIR_OPPOSITE(direction);
14327     int hitting_side = direction;
14328     int touched_side = opposite_direction;
14329     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14330                           MovDir[hitx][hity] != direction ||
14331                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14332
14333     object_hit = TRUE;
14334
14335     if (object_hit)
14336     {
14337       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14338                                CE_HITTING_X, touched_side);
14339
14340       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14341                                CE_HIT_BY_X, hitting_side);
14342
14343       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14344                                CE_HIT_BY_SOMETHING, opposite_direction);
14345
14346 #if USE_FIX_CE_ACTION_WITH_PLAYER
14347       if (IS_PLAYER(hitx, hity))
14348       {
14349         /* use player element that is initially defined in the level playfield,
14350            not the player element that corresponds to the runtime player number
14351            (example: a level that contains EL_PLAYER_3 as the only player would
14352            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14353         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14354
14355         CheckElementChangeBySide(x, y, hitting_element, player_element,
14356                                  CE_HITTING_X, touched_side);
14357       }
14358 #endif
14359     }
14360   }
14361
14362   /* "hitting something" is also true when hitting the playfield border */
14363   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14364                            CE_HITTING_SOMETHING, direction);
14365 }
14366
14367 #if 0
14368 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14369 {
14370   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14371   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14372   int hitx = x + dx, hity = y + dy;
14373   int hitting_element = Feld[x][y];
14374   int touched_element;
14375 #if 0
14376   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14377                         !IS_FREE(hitx, hity) &&
14378                         (!IS_MOVING(hitx, hity) ||
14379                          MovDir[hitx][hity] != direction ||
14380                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14381 #endif
14382
14383   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14384     return;
14385
14386 #if 0
14387   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14388     return;
14389 #endif
14390
14391   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14392                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14393
14394   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14395                            EP_CAN_SMASH_EVERYTHING, direction);
14396
14397   if (IN_LEV_FIELD(hitx, hity))
14398   {
14399     int opposite_direction = MV_DIR_OPPOSITE(direction);
14400     int hitting_side = direction;
14401     int touched_side = opposite_direction;
14402 #if 0
14403     int touched_element = MovingOrBlocked2Element(hitx, hity);
14404 #endif
14405 #if 1
14406     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14407                           MovDir[hitx][hity] != direction ||
14408                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14409
14410     object_hit = TRUE;
14411 #endif
14412
14413     if (object_hit)
14414     {
14415       int i;
14416
14417       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14418                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14419
14420       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14421                                CE_OTHER_IS_SMASHING, touched_side);
14422
14423       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14424                                CE_OTHER_GETS_SMASHED, hitting_side);
14425     }
14426   }
14427 }
14428 #endif
14429
14430 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14431 {
14432   int i, kill_x = -1, kill_y = -1;
14433
14434   int bad_element = -1;
14435   static int test_xy[4][2] =
14436   {
14437     { 0, -1 },
14438     { -1, 0 },
14439     { +1, 0 },
14440     { 0, +1 }
14441   };
14442   static int test_dir[4] =
14443   {
14444     MV_UP,
14445     MV_LEFT,
14446     MV_RIGHT,
14447     MV_DOWN
14448   };
14449
14450   for (i = 0; i < NUM_DIRECTIONS; i++)
14451   {
14452     int test_x, test_y, test_move_dir, test_element;
14453
14454     test_x = good_x + test_xy[i][0];
14455     test_y = good_y + test_xy[i][1];
14456
14457     if (!IN_LEV_FIELD(test_x, test_y))
14458       continue;
14459
14460     test_move_dir =
14461       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14462
14463     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14464
14465     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14466        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14467     */
14468     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14469         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14470     {
14471       kill_x = test_x;
14472       kill_y = test_y;
14473       bad_element = test_element;
14474
14475       break;
14476     }
14477   }
14478
14479   if (kill_x != -1 || kill_y != -1)
14480   {
14481     if (IS_PLAYER(good_x, good_y))
14482     {
14483       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14484
14485       if (player->shield_deadly_time_left > 0 &&
14486           !IS_INDESTRUCTIBLE(bad_element))
14487         Bang(kill_x, kill_y);
14488       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14489         KillPlayer(player);
14490     }
14491     else
14492       Bang(good_x, good_y);
14493   }
14494 }
14495
14496 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14497 {
14498   int i, kill_x = -1, kill_y = -1;
14499   int bad_element = Feld[bad_x][bad_y];
14500   static int test_xy[4][2] =
14501   {
14502     { 0, -1 },
14503     { -1, 0 },
14504     { +1, 0 },
14505     { 0, +1 }
14506   };
14507   static int touch_dir[4] =
14508   {
14509     MV_LEFT | MV_RIGHT,
14510     MV_UP   | MV_DOWN,
14511     MV_UP   | MV_DOWN,
14512     MV_LEFT | MV_RIGHT
14513   };
14514   static int test_dir[4] =
14515   {
14516     MV_UP,
14517     MV_LEFT,
14518     MV_RIGHT,
14519     MV_DOWN
14520   };
14521
14522   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14523     return;
14524
14525   for (i = 0; i < NUM_DIRECTIONS; i++)
14526   {
14527     int test_x, test_y, test_move_dir, test_element;
14528
14529     test_x = bad_x + test_xy[i][0];
14530     test_y = bad_y + test_xy[i][1];
14531
14532     if (!IN_LEV_FIELD(test_x, test_y))
14533       continue;
14534
14535     test_move_dir =
14536       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14537
14538     test_element = Feld[test_x][test_y];
14539
14540     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14541        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14542     */
14543     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14544         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14545     {
14546       /* good thing is player or penguin that does not move away */
14547       if (IS_PLAYER(test_x, test_y))
14548       {
14549         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14550
14551         if (bad_element == EL_ROBOT && player->is_moving)
14552           continue;     /* robot does not kill player if he is moving */
14553
14554         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14555         {
14556           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14557             continue;           /* center and border element do not touch */
14558         }
14559
14560         kill_x = test_x;
14561         kill_y = test_y;
14562
14563         break;
14564       }
14565       else if (test_element == EL_PENGUIN)
14566       {
14567         kill_x = test_x;
14568         kill_y = test_y;
14569
14570         break;
14571       }
14572     }
14573   }
14574
14575   if (kill_x != -1 || kill_y != -1)
14576   {
14577     if (IS_PLAYER(kill_x, kill_y))
14578     {
14579       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14580
14581       if (player->shield_deadly_time_left > 0 &&
14582           !IS_INDESTRUCTIBLE(bad_element))
14583         Bang(bad_x, bad_y);
14584       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14585         KillPlayer(player);
14586     }
14587     else
14588       Bang(kill_x, kill_y);
14589   }
14590 }
14591
14592 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14593 {
14594   int bad_element = Feld[bad_x][bad_y];
14595   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14596   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14597   int test_x = bad_x + dx, test_y = bad_y + dy;
14598   int test_move_dir, test_element;
14599   int kill_x = -1, kill_y = -1;
14600
14601   if (!IN_LEV_FIELD(test_x, test_y))
14602     return;
14603
14604   test_move_dir =
14605     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14606
14607   test_element = Feld[test_x][test_y];
14608
14609   if (test_move_dir != bad_move_dir)
14610   {
14611     /* good thing can be player or penguin that does not move away */
14612     if (IS_PLAYER(test_x, test_y))
14613     {
14614       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14615
14616       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14617          player as being hit when he is moving towards the bad thing, because
14618          the "get hit by" condition would be lost after the player stops) */
14619       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14620         return;         /* player moves away from bad thing */
14621
14622       kill_x = test_x;
14623       kill_y = test_y;
14624     }
14625     else if (test_element == EL_PENGUIN)
14626     {
14627       kill_x = test_x;
14628       kill_y = test_y;
14629     }
14630   }
14631
14632   if (kill_x != -1 || kill_y != -1)
14633   {
14634     if (IS_PLAYER(kill_x, kill_y))
14635     {
14636       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14637
14638       if (player->shield_deadly_time_left > 0 &&
14639           !IS_INDESTRUCTIBLE(bad_element))
14640         Bang(bad_x, bad_y);
14641       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14642         KillPlayer(player);
14643     }
14644     else
14645       Bang(kill_x, kill_y);
14646   }
14647 }
14648
14649 void TestIfPlayerTouchesBadThing(int x, int y)
14650 {
14651   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14652 }
14653
14654 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14655 {
14656   TestIfGoodThingHitsBadThing(x, y, move_dir);
14657 }
14658
14659 void TestIfBadThingTouchesPlayer(int x, int y)
14660 {
14661   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14662 }
14663
14664 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14665 {
14666   TestIfBadThingHitsGoodThing(x, y, move_dir);
14667 }
14668
14669 void TestIfFriendTouchesBadThing(int x, int y)
14670 {
14671   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14672 }
14673
14674 void TestIfBadThingTouchesFriend(int x, int y)
14675 {
14676   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14677 }
14678
14679 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14680 {
14681   int i, kill_x = bad_x, kill_y = bad_y;
14682   static int xy[4][2] =
14683   {
14684     { 0, -1 },
14685     { -1, 0 },
14686     { +1, 0 },
14687     { 0, +1 }
14688   };
14689
14690   for (i = 0; i < NUM_DIRECTIONS; i++)
14691   {
14692     int x, y, element;
14693
14694     x = bad_x + xy[i][0];
14695     y = bad_y + xy[i][1];
14696     if (!IN_LEV_FIELD(x, y))
14697       continue;
14698
14699     element = Feld[x][y];
14700     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14701         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14702     {
14703       kill_x = x;
14704       kill_y = y;
14705       break;
14706     }
14707   }
14708
14709   if (kill_x != bad_x || kill_y != bad_y)
14710     Bang(bad_x, bad_y);
14711 }
14712
14713 void KillPlayer(struct PlayerInfo *player)
14714 {
14715   int jx = player->jx, jy = player->jy;
14716
14717   if (!player->active)
14718     return;
14719
14720 #if 0
14721   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14722          player->killed, player->active, player->reanimated);
14723 #endif
14724
14725   /* the following code was introduced to prevent an infinite loop when calling
14726      -> Bang()
14727      -> CheckTriggeredElementChangeExt()
14728      -> ExecuteCustomElementAction()
14729      -> KillPlayer()
14730      -> (infinitely repeating the above sequence of function calls)
14731      which occurs when killing the player while having a CE with the setting
14732      "kill player X when explosion of <player X>"; the solution using a new
14733      field "player->killed" was chosen for backwards compatibility, although
14734      clever use of the fields "player->active" etc. would probably also work */
14735 #if 1
14736   if (player->killed)
14737     return;
14738 #endif
14739
14740   player->killed = TRUE;
14741
14742   /* remove accessible field at the player's position */
14743   Feld[jx][jy] = EL_EMPTY;
14744
14745   /* deactivate shield (else Bang()/Explode() would not work right) */
14746   player->shield_normal_time_left = 0;
14747   player->shield_deadly_time_left = 0;
14748
14749 #if 0
14750   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14751          player->killed, player->active, player->reanimated);
14752 #endif
14753
14754   Bang(jx, jy);
14755
14756 #if 0
14757   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14758          player->killed, player->active, player->reanimated);
14759 #endif
14760
14761 #if USE_PLAYER_REANIMATION
14762 #if 1
14763   if (player->reanimated)       /* killed player may have been reanimated */
14764     player->killed = player->reanimated = FALSE;
14765   else
14766     BuryPlayer(player);
14767 #else
14768   if (player->killed)           /* player may have been reanimated */
14769     BuryPlayer(player);
14770 #endif
14771 #else
14772   BuryPlayer(player);
14773 #endif
14774 }
14775
14776 static void KillPlayerUnlessEnemyProtected(int x, int y)
14777 {
14778   if (!PLAYER_ENEMY_PROTECTED(x, y))
14779     KillPlayer(PLAYERINFO(x, y));
14780 }
14781
14782 static void KillPlayerUnlessExplosionProtected(int x, int y)
14783 {
14784   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14785     KillPlayer(PLAYERINFO(x, y));
14786 }
14787
14788 void BuryPlayer(struct PlayerInfo *player)
14789 {
14790   int jx = player->jx, jy = player->jy;
14791
14792   if (!player->active)
14793     return;
14794
14795   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14796   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14797
14798   player->GameOver = TRUE;
14799   RemovePlayer(player);
14800 }
14801
14802 void RemovePlayer(struct PlayerInfo *player)
14803 {
14804   int jx = player->jx, jy = player->jy;
14805   int i, found = FALSE;
14806
14807   player->present = FALSE;
14808   player->active = FALSE;
14809
14810   if (!ExplodeField[jx][jy])
14811     StorePlayer[jx][jy] = 0;
14812
14813   if (player->is_moving)
14814     TEST_DrawLevelField(player->last_jx, player->last_jy);
14815
14816   for (i = 0; i < MAX_PLAYERS; i++)
14817     if (stored_player[i].active)
14818       found = TRUE;
14819
14820   if (!found)
14821     AllPlayersGone = TRUE;
14822
14823   ExitX = ZX = jx;
14824   ExitY = ZY = jy;
14825 }
14826
14827 #if USE_NEW_SNAP_DELAY
14828 static void setFieldForSnapping(int x, int y, int element, int direction)
14829 {
14830   struct ElementInfo *ei = &element_info[element];
14831   int direction_bit = MV_DIR_TO_BIT(direction);
14832   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14833   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14834                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14835
14836   Feld[x][y] = EL_ELEMENT_SNAPPING;
14837   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14838
14839   ResetGfxAnimation(x, y);
14840
14841   GfxElement[x][y] = element;
14842   GfxAction[x][y] = action;
14843   GfxDir[x][y] = direction;
14844   GfxFrame[x][y] = -1;
14845 }
14846 #endif
14847
14848 /*
14849   =============================================================================
14850   checkDiagonalPushing()
14851   -----------------------------------------------------------------------------
14852   check if diagonal input device direction results in pushing of object
14853   (by checking if the alternative direction is walkable, diggable, ...)
14854   =============================================================================
14855 */
14856
14857 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14858                                     int x, int y, int real_dx, int real_dy)
14859 {
14860   int jx, jy, dx, dy, xx, yy;
14861
14862   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14863     return TRUE;
14864
14865   /* diagonal direction: check alternative direction */
14866   jx = player->jx;
14867   jy = player->jy;
14868   dx = x - jx;
14869   dy = y - jy;
14870   xx = jx + (dx == 0 ? real_dx : 0);
14871   yy = jy + (dy == 0 ? real_dy : 0);
14872
14873   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14874 }
14875
14876 /*
14877   =============================================================================
14878   DigField()
14879   -----------------------------------------------------------------------------
14880   x, y:                 field next to player (non-diagonal) to try to dig to
14881   real_dx, real_dy:     direction as read from input device (can be diagonal)
14882   =============================================================================
14883 */
14884
14885 static int DigField(struct PlayerInfo *player,
14886                     int oldx, int oldy, int x, int y,
14887                     int real_dx, int real_dy, int mode)
14888 {
14889   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14890   boolean player_was_pushing = player->is_pushing;
14891   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14892   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14893   int jx = oldx, jy = oldy;
14894   int dx = x - jx, dy = y - jy;
14895   int nextx = x + dx, nexty = y + dy;
14896   int move_direction = (dx == -1 ? MV_LEFT  :
14897                         dx == +1 ? MV_RIGHT :
14898                         dy == -1 ? MV_UP    :
14899                         dy == +1 ? MV_DOWN  : MV_NONE);
14900   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14901   int dig_side = MV_DIR_OPPOSITE(move_direction);
14902   int old_element = Feld[jx][jy];
14903 #if USE_FIXED_DONT_RUN_INTO
14904   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14905 #else
14906   int element;
14907 #endif
14908   int collect_count;
14909
14910   if (is_player)                /* function can also be called by EL_PENGUIN */
14911   {
14912     if (player->MovPos == 0)
14913     {
14914       player->is_digging = FALSE;
14915       player->is_collecting = FALSE;
14916     }
14917
14918     if (player->MovPos == 0)    /* last pushing move finished */
14919       player->is_pushing = FALSE;
14920
14921     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14922     {
14923       player->is_switching = FALSE;
14924       player->push_delay = -1;
14925
14926       return MP_NO_ACTION;
14927     }
14928   }
14929
14930 #if !USE_FIXED_DONT_RUN_INTO
14931   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14932     return MP_NO_ACTION;
14933 #endif
14934
14935   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14936     old_element = Back[jx][jy];
14937
14938   /* in case of element dropped at player position, check background */
14939   else if (Back[jx][jy] != EL_EMPTY &&
14940            game.engine_version >= VERSION_IDENT(2,2,0,0))
14941     old_element = Back[jx][jy];
14942
14943   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14944     return MP_NO_ACTION;        /* field has no opening in this direction */
14945
14946   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14947     return MP_NO_ACTION;        /* field has no opening in this direction */
14948
14949 #if USE_FIXED_DONT_RUN_INTO
14950   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14951   {
14952     SplashAcid(x, y);
14953
14954     Feld[jx][jy] = player->artwork_element;
14955     InitMovingField(jx, jy, MV_DOWN);
14956     Store[jx][jy] = EL_ACID;
14957     ContinueMoving(jx, jy);
14958     BuryPlayer(player);
14959
14960     return MP_DONT_RUN_INTO;
14961   }
14962 #endif
14963
14964 #if USE_FIXED_DONT_RUN_INTO
14965   if (player_can_move && DONT_RUN_INTO(element))
14966   {
14967     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14968
14969     return MP_DONT_RUN_INTO;
14970   }
14971 #endif
14972
14973 #if USE_FIXED_DONT_RUN_INTO
14974   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14975     return MP_NO_ACTION;
14976 #endif
14977
14978 #if !USE_FIXED_DONT_RUN_INTO
14979   element = Feld[x][y];
14980 #endif
14981
14982   collect_count = element_info[element].collect_count_initial;
14983
14984   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14985     return MP_NO_ACTION;
14986
14987   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14988     player_can_move = player_can_move_or_snap;
14989
14990   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14991       game.engine_version >= VERSION_IDENT(2,2,0,0))
14992   {
14993     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14994                                player->index_bit, dig_side);
14995     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14996                                         player->index_bit, dig_side);
14997
14998     if (element == EL_DC_LANDMINE)
14999       Bang(x, y);
15000
15001     if (Feld[x][y] != element)          /* field changed by snapping */
15002       return MP_ACTION;
15003
15004     return MP_NO_ACTION;
15005   }
15006
15007 #if USE_PLAYER_GRAVITY
15008   if (player->gravity && is_player && !player->is_auto_moving &&
15009       canFallDown(player) && move_direction != MV_DOWN &&
15010       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15011     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15012 #else
15013   if (game.gravity && is_player && !player->is_auto_moving &&
15014       canFallDown(player) && move_direction != MV_DOWN &&
15015       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15016     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15017 #endif
15018
15019   if (player_can_move &&
15020       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15021   {
15022     int sound_element = SND_ELEMENT(element);
15023     int sound_action = ACTION_WALKING;
15024
15025     if (IS_RND_GATE(element))
15026     {
15027       if (!player->key[RND_GATE_NR(element)])
15028         return MP_NO_ACTION;
15029     }
15030     else if (IS_RND_GATE_GRAY(element))
15031     {
15032       if (!player->key[RND_GATE_GRAY_NR(element)])
15033         return MP_NO_ACTION;
15034     }
15035     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15036     {
15037       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15038         return MP_NO_ACTION;
15039     }
15040     else if (element == EL_EXIT_OPEN ||
15041              element == EL_EM_EXIT_OPEN ||
15042 #if 1
15043              element == EL_EM_EXIT_OPENING ||
15044 #endif
15045              element == EL_STEEL_EXIT_OPEN ||
15046              element == EL_EM_STEEL_EXIT_OPEN ||
15047 #if 1
15048              element == EL_EM_STEEL_EXIT_OPENING ||
15049 #endif
15050              element == EL_SP_EXIT_OPEN ||
15051              element == EL_SP_EXIT_OPENING)
15052     {
15053       sound_action = ACTION_PASSING;    /* player is passing exit */
15054     }
15055     else if (element == EL_EMPTY)
15056     {
15057       sound_action = ACTION_MOVING;             /* nothing to walk on */
15058     }
15059
15060     /* play sound from background or player, whatever is available */
15061     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15062       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15063     else
15064       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15065   }
15066   else if (player_can_move &&
15067            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15068   {
15069     if (!ACCESS_FROM(element, opposite_direction))
15070       return MP_NO_ACTION;      /* field not accessible from this direction */
15071
15072     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15073       return MP_NO_ACTION;
15074
15075     if (IS_EM_GATE(element))
15076     {
15077       if (!player->key[EM_GATE_NR(element)])
15078         return MP_NO_ACTION;
15079     }
15080     else if (IS_EM_GATE_GRAY(element))
15081     {
15082       if (!player->key[EM_GATE_GRAY_NR(element)])
15083         return MP_NO_ACTION;
15084     }
15085     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15086     {
15087       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15088         return MP_NO_ACTION;
15089     }
15090     else if (IS_EMC_GATE(element))
15091     {
15092       if (!player->key[EMC_GATE_NR(element)])
15093         return MP_NO_ACTION;
15094     }
15095     else if (IS_EMC_GATE_GRAY(element))
15096     {
15097       if (!player->key[EMC_GATE_GRAY_NR(element)])
15098         return MP_NO_ACTION;
15099     }
15100     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15101     {
15102       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15103         return MP_NO_ACTION;
15104     }
15105     else if (element == EL_DC_GATE_WHITE ||
15106              element == EL_DC_GATE_WHITE_GRAY ||
15107              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15108     {
15109       if (player->num_white_keys == 0)
15110         return MP_NO_ACTION;
15111
15112       player->num_white_keys--;
15113     }
15114     else if (IS_SP_PORT(element))
15115     {
15116       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15117           element == EL_SP_GRAVITY_PORT_RIGHT ||
15118           element == EL_SP_GRAVITY_PORT_UP ||
15119           element == EL_SP_GRAVITY_PORT_DOWN)
15120 #if USE_PLAYER_GRAVITY
15121         player->gravity = !player->gravity;
15122 #else
15123         game.gravity = !game.gravity;
15124 #endif
15125       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15126                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15127                element == EL_SP_GRAVITY_ON_PORT_UP ||
15128                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130         player->gravity = TRUE;
15131 #else
15132         game.gravity = TRUE;
15133 #endif
15134       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15135                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15136                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15137                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139         player->gravity = FALSE;
15140 #else
15141         game.gravity = FALSE;
15142 #endif
15143     }
15144
15145     /* automatically move to the next field with double speed */
15146     player->programmed_action = move_direction;
15147
15148     if (player->move_delay_reset_counter == 0)
15149     {
15150       player->move_delay_reset_counter = 2;     /* two double speed steps */
15151
15152       DOUBLE_PLAYER_SPEED(player);
15153     }
15154
15155     PlayLevelSoundAction(x, y, ACTION_PASSING);
15156   }
15157   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15158   {
15159     RemoveField(x, y);
15160
15161     if (mode != DF_SNAP)
15162     {
15163       GfxElement[x][y] = GFX_ELEMENT(element);
15164       player->is_digging = TRUE;
15165     }
15166
15167     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15168
15169     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15170                                         player->index_bit, dig_side);
15171
15172     if (mode == DF_SNAP)
15173     {
15174 #if USE_NEW_SNAP_DELAY
15175       if (level.block_snap_field)
15176         setFieldForSnapping(x, y, element, move_direction);
15177       else
15178         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15179 #else
15180       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15181 #endif
15182
15183       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15184                                           player->index_bit, dig_side);
15185     }
15186   }
15187   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15188   {
15189     RemoveField(x, y);
15190
15191     if (is_player && mode != DF_SNAP)
15192     {
15193       GfxElement[x][y] = element;
15194       player->is_collecting = TRUE;
15195     }
15196
15197     if (element == EL_SPEED_PILL)
15198     {
15199       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15200     }
15201     else if (element == EL_EXTRA_TIME && level.time > 0)
15202     {
15203       TimeLeft += level.extra_time;
15204
15205 #if 1
15206       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15207
15208       DisplayGameControlValues();
15209 #else
15210       DrawGameValue_Time(TimeLeft);
15211 #endif
15212     }
15213     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15214     {
15215       player->shield_normal_time_left += level.shield_normal_time;
15216       if (element == EL_SHIELD_DEADLY)
15217         player->shield_deadly_time_left += level.shield_deadly_time;
15218     }
15219     else if (element == EL_DYNAMITE ||
15220              element == EL_EM_DYNAMITE ||
15221              element == EL_SP_DISK_RED)
15222     {
15223       if (player->inventory_size < MAX_INVENTORY_SIZE)
15224         player->inventory_element[player->inventory_size++] = element;
15225
15226       DrawGameDoorValues();
15227     }
15228     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15229     {
15230       player->dynabomb_count++;
15231       player->dynabombs_left++;
15232     }
15233     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15234     {
15235       player->dynabomb_size++;
15236     }
15237     else if (element == EL_DYNABOMB_INCREASE_POWER)
15238     {
15239       player->dynabomb_xl = TRUE;
15240     }
15241     else if (IS_KEY(element))
15242     {
15243       player->key[KEY_NR(element)] = TRUE;
15244
15245       DrawGameDoorValues();
15246     }
15247     else if (element == EL_DC_KEY_WHITE)
15248     {
15249       player->num_white_keys++;
15250
15251       /* display white keys? */
15252       /* DrawGameDoorValues(); */
15253     }
15254     else if (IS_ENVELOPE(element))
15255     {
15256       player->show_envelope = element;
15257     }
15258     else if (element == EL_EMC_LENSES)
15259     {
15260       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15261
15262       RedrawAllInvisibleElementsForLenses();
15263     }
15264     else if (element == EL_EMC_MAGNIFIER)
15265     {
15266       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15267
15268       RedrawAllInvisibleElementsForMagnifier();
15269     }
15270     else if (IS_DROPPABLE(element) ||
15271              IS_THROWABLE(element))     /* can be collected and dropped */
15272     {
15273       int i;
15274
15275       if (collect_count == 0)
15276         player->inventory_infinite_element = element;
15277       else
15278         for (i = 0; i < collect_count; i++)
15279           if (player->inventory_size < MAX_INVENTORY_SIZE)
15280             player->inventory_element[player->inventory_size++] = element;
15281
15282       DrawGameDoorValues();
15283     }
15284     else if (collect_count > 0)
15285     {
15286       local_player->gems_still_needed -= collect_count;
15287       if (local_player->gems_still_needed < 0)
15288         local_player->gems_still_needed = 0;
15289
15290 #if 1
15291       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15292
15293       DisplayGameControlValues();
15294 #else
15295       DrawGameValue_Emeralds(local_player->gems_still_needed);
15296 #endif
15297     }
15298
15299     RaiseScoreElement(element);
15300     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15301
15302     if (is_player)
15303       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15304                                           player->index_bit, dig_side);
15305
15306     if (mode == DF_SNAP)
15307     {
15308 #if USE_NEW_SNAP_DELAY
15309       if (level.block_snap_field)
15310         setFieldForSnapping(x, y, element, move_direction);
15311       else
15312         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15313 #else
15314       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15315 #endif
15316
15317       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15318                                           player->index_bit, dig_side);
15319     }
15320   }
15321   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15322   {
15323     if (mode == DF_SNAP && element != EL_BD_ROCK)
15324       return MP_NO_ACTION;
15325
15326     if (CAN_FALL(element) && dy)
15327       return MP_NO_ACTION;
15328
15329     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15330         !(element == EL_SPRING && level.use_spring_bug))
15331       return MP_NO_ACTION;
15332
15333     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15334         ((move_direction & MV_VERTICAL &&
15335           ((element_info[element].move_pattern & MV_LEFT &&
15336             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15337            (element_info[element].move_pattern & MV_RIGHT &&
15338             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15339          (move_direction & MV_HORIZONTAL &&
15340           ((element_info[element].move_pattern & MV_UP &&
15341             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15342            (element_info[element].move_pattern & MV_DOWN &&
15343             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15344       return MP_NO_ACTION;
15345
15346     /* do not push elements already moving away faster than player */
15347     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15348         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15349       return MP_NO_ACTION;
15350
15351     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15352     {
15353       if (player->push_delay_value == -1 || !player_was_pushing)
15354         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15355     }
15356     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15357     {
15358       if (player->push_delay_value == -1)
15359         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15360     }
15361     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15362     {
15363       if (!player->is_pushing)
15364         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15365     }
15366
15367     player->is_pushing = TRUE;
15368     player->is_active = TRUE;
15369
15370     if (!(IN_LEV_FIELD(nextx, nexty) &&
15371           (IS_FREE(nextx, nexty) ||
15372            (IS_SB_ELEMENT(element) &&
15373             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15374            (IS_CUSTOM_ELEMENT(element) &&
15375             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15376       return MP_NO_ACTION;
15377
15378     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15379       return MP_NO_ACTION;
15380
15381     if (player->push_delay == -1)       /* new pushing; restart delay */
15382       player->push_delay = 0;
15383
15384     if (player->push_delay < player->push_delay_value &&
15385         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15386         element != EL_SPRING && element != EL_BALLOON)
15387     {
15388       /* make sure that there is no move delay before next try to push */
15389       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15390         player->move_delay = 0;
15391
15392       return MP_NO_ACTION;
15393     }
15394
15395     if (IS_CUSTOM_ELEMENT(element) &&
15396         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15397     {
15398       if (!DigFieldByCE(nextx, nexty, element))
15399         return MP_NO_ACTION;
15400     }
15401
15402     if (IS_SB_ELEMENT(element))
15403     {
15404       if (element == EL_SOKOBAN_FIELD_FULL)
15405       {
15406         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15407         local_player->sokobanfields_still_needed++;
15408       }
15409
15410       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15411       {
15412         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15413         local_player->sokobanfields_still_needed--;
15414       }
15415
15416       Feld[x][y] = EL_SOKOBAN_OBJECT;
15417
15418       if (Back[x][y] == Back[nextx][nexty])
15419         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15420       else if (Back[x][y] != 0)
15421         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15422                                     ACTION_EMPTYING);
15423       else
15424         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15425                                     ACTION_FILLING);
15426
15427 #if 1
15428       if (local_player->sokobanfields_still_needed == 0 &&
15429           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15430 #else
15431       if (local_player->sokobanfields_still_needed == 0 &&
15432           game.emulation == EMU_SOKOBAN)
15433 #endif
15434       {
15435         PlayerWins(player);
15436
15437         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15438       }
15439     }
15440     else
15441       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15442
15443     InitMovingField(x, y, move_direction);
15444     GfxAction[x][y] = ACTION_PUSHING;
15445
15446     if (mode == DF_SNAP)
15447       ContinueMoving(x, y);
15448     else
15449       MovPos[x][y] = (dx != 0 ? dx : dy);
15450
15451     Pushed[x][y] = TRUE;
15452     Pushed[nextx][nexty] = TRUE;
15453
15454     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15455       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15456     else
15457       player->push_delay_value = -1;    /* get new value later */
15458
15459     /* check for element change _after_ element has been pushed */
15460     if (game.use_change_when_pushing_bug)
15461     {
15462       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15463                                  player->index_bit, dig_side);
15464       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15465                                           player->index_bit, dig_side);
15466     }
15467   }
15468   else if (IS_SWITCHABLE(element))
15469   {
15470     if (PLAYER_SWITCHING(player, x, y))
15471     {
15472       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15473                                           player->index_bit, dig_side);
15474
15475       return MP_ACTION;
15476     }
15477
15478     player->is_switching = TRUE;
15479     player->switch_x = x;
15480     player->switch_y = y;
15481
15482     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15483
15484     if (element == EL_ROBOT_WHEEL)
15485     {
15486       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15487       ZX = x;
15488       ZY = y;
15489
15490       game.robot_wheel_active = TRUE;
15491
15492       TEST_DrawLevelField(x, y);
15493     }
15494     else if (element == EL_SP_TERMINAL)
15495     {
15496       int xx, yy;
15497
15498       SCAN_PLAYFIELD(xx, yy)
15499       {
15500         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15501           Bang(xx, yy);
15502         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15503           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15504       }
15505     }
15506     else if (IS_BELT_SWITCH(element))
15507     {
15508       ToggleBeltSwitch(x, y);
15509     }
15510     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15511              element == EL_SWITCHGATE_SWITCH_DOWN ||
15512              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15513              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15514     {
15515       ToggleSwitchgateSwitch(x, y);
15516     }
15517     else if (element == EL_LIGHT_SWITCH ||
15518              element == EL_LIGHT_SWITCH_ACTIVE)
15519     {
15520       ToggleLightSwitch(x, y);
15521     }
15522     else if (element == EL_TIMEGATE_SWITCH ||
15523              element == EL_DC_TIMEGATE_SWITCH)
15524     {
15525       ActivateTimegateSwitch(x, y);
15526     }
15527     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15528              element == EL_BALLOON_SWITCH_RIGHT ||
15529              element == EL_BALLOON_SWITCH_UP    ||
15530              element == EL_BALLOON_SWITCH_DOWN  ||
15531              element == EL_BALLOON_SWITCH_NONE  ||
15532              element == EL_BALLOON_SWITCH_ANY)
15533     {
15534       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15535                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15536                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15537                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15538                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15539                              move_direction);
15540     }
15541     else if (element == EL_LAMP)
15542     {
15543       Feld[x][y] = EL_LAMP_ACTIVE;
15544       local_player->lights_still_needed--;
15545
15546       ResetGfxAnimation(x, y);
15547       TEST_DrawLevelField(x, y);
15548     }
15549     else if (element == EL_TIME_ORB_FULL)
15550     {
15551       Feld[x][y] = EL_TIME_ORB_EMPTY;
15552
15553       if (level.time > 0 || level.use_time_orb_bug)
15554       {
15555         TimeLeft += level.time_orb_time;
15556
15557 #if 1
15558         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15559
15560         DisplayGameControlValues();
15561 #else
15562         DrawGameValue_Time(TimeLeft);
15563 #endif
15564       }
15565
15566       ResetGfxAnimation(x, y);
15567       TEST_DrawLevelField(x, y);
15568     }
15569     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15570              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15571     {
15572       int xx, yy;
15573
15574       game.ball_state = !game.ball_state;
15575
15576       SCAN_PLAYFIELD(xx, yy)
15577       {
15578         int e = Feld[xx][yy];
15579
15580         if (game.ball_state)
15581         {
15582           if (e == EL_EMC_MAGIC_BALL)
15583             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15584           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15585             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15586         }
15587         else
15588         {
15589           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15590             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15591           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15592             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15593         }
15594       }
15595     }
15596
15597     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15598                                         player->index_bit, dig_side);
15599
15600     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15601                                         player->index_bit, dig_side);
15602
15603     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15604                                         player->index_bit, dig_side);
15605
15606     return MP_ACTION;
15607   }
15608   else
15609   {
15610     if (!PLAYER_SWITCHING(player, x, y))
15611     {
15612       player->is_switching = TRUE;
15613       player->switch_x = x;
15614       player->switch_y = y;
15615
15616       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15617                                  player->index_bit, dig_side);
15618       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15619                                           player->index_bit, dig_side);
15620
15621       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15622                                  player->index_bit, dig_side);
15623       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15624                                           player->index_bit, dig_side);
15625     }
15626
15627     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15628                                player->index_bit, dig_side);
15629     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15630                                         player->index_bit, dig_side);
15631
15632     return MP_NO_ACTION;
15633   }
15634
15635   player->push_delay = -1;
15636
15637   if (is_player)                /* function can also be called by EL_PENGUIN */
15638   {
15639     if (Feld[x][y] != element)          /* really digged/collected something */
15640     {
15641       player->is_collecting = !player->is_digging;
15642       player->is_active = TRUE;
15643     }
15644   }
15645
15646   return MP_MOVING;
15647 }
15648
15649 static boolean DigFieldByCE(int x, int y, int digging_element)
15650 {
15651   int element = Feld[x][y];
15652
15653   if (!IS_FREE(x, y))
15654   {
15655     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15656                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15657                   ACTION_BREAKING);
15658
15659     /* no element can dig solid indestructible elements */
15660     if (IS_INDESTRUCTIBLE(element) &&
15661         !IS_DIGGABLE(element) &&
15662         !IS_COLLECTIBLE(element))
15663       return FALSE;
15664
15665     if (AmoebaNr[x][y] &&
15666         (element == EL_AMOEBA_FULL ||
15667          element == EL_BD_AMOEBA ||
15668          element == EL_AMOEBA_GROWING))
15669     {
15670       AmoebaCnt[AmoebaNr[x][y]]--;
15671       AmoebaCnt2[AmoebaNr[x][y]]--;
15672     }
15673
15674     if (IS_MOVING(x, y))
15675       RemoveMovingField(x, y);
15676     else
15677     {
15678       RemoveField(x, y);
15679       TEST_DrawLevelField(x, y);
15680     }
15681
15682     /* if digged element was about to explode, prevent the explosion */
15683     ExplodeField[x][y] = EX_TYPE_NONE;
15684
15685     PlayLevelSoundAction(x, y, action);
15686   }
15687
15688   Store[x][y] = EL_EMPTY;
15689
15690 #if 1
15691   /* this makes it possible to leave the removed element again */
15692   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15693     Store[x][y] = element;
15694 #else
15695   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15696   {
15697     int move_leave_element = element_info[digging_element].move_leave_element;
15698
15699     /* this makes it possible to leave the removed element again */
15700     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15701                    element : move_leave_element);
15702   }
15703 #endif
15704
15705   return TRUE;
15706 }
15707
15708 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15709 {
15710   int jx = player->jx, jy = player->jy;
15711   int x = jx + dx, y = jy + dy;
15712   int snap_direction = (dx == -1 ? MV_LEFT  :
15713                         dx == +1 ? MV_RIGHT :
15714                         dy == -1 ? MV_UP    :
15715                         dy == +1 ? MV_DOWN  : MV_NONE);
15716   boolean can_continue_snapping = (level.continuous_snapping &&
15717                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15718
15719   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15720     return FALSE;
15721
15722   if (!player->active || !IN_LEV_FIELD(x, y))
15723     return FALSE;
15724
15725   if (dx && dy)
15726     return FALSE;
15727
15728   if (!dx && !dy)
15729   {
15730     if (player->MovPos == 0)
15731       player->is_pushing = FALSE;
15732
15733     player->is_snapping = FALSE;
15734
15735     if (player->MovPos == 0)
15736     {
15737       player->is_moving = FALSE;
15738       player->is_digging = FALSE;
15739       player->is_collecting = FALSE;
15740     }
15741
15742     return FALSE;
15743   }
15744
15745 #if USE_NEW_CONTINUOUS_SNAPPING
15746   /* prevent snapping with already pressed snap key when not allowed */
15747   if (player->is_snapping && !can_continue_snapping)
15748     return FALSE;
15749 #else
15750   if (player->is_snapping)
15751     return FALSE;
15752 #endif
15753
15754   player->MovDir = snap_direction;
15755
15756   if (player->MovPos == 0)
15757   {
15758     player->is_moving = FALSE;
15759     player->is_digging = FALSE;
15760     player->is_collecting = FALSE;
15761   }
15762
15763   player->is_dropping = FALSE;
15764   player->is_dropping_pressed = FALSE;
15765   player->drop_pressed_delay = 0;
15766
15767   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15768     return FALSE;
15769
15770   player->is_snapping = TRUE;
15771   player->is_active = TRUE;
15772
15773   if (player->MovPos == 0)
15774   {
15775     player->is_moving = FALSE;
15776     player->is_digging = FALSE;
15777     player->is_collecting = FALSE;
15778   }
15779
15780   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15781     TEST_DrawLevelField(player->last_jx, player->last_jy);
15782
15783   TEST_DrawLevelField(x, y);
15784
15785   return TRUE;
15786 }
15787
15788 static boolean DropElement(struct PlayerInfo *player)
15789 {
15790   int old_element, new_element;
15791   int dropx = player->jx, dropy = player->jy;
15792   int drop_direction = player->MovDir;
15793   int drop_side = drop_direction;
15794 #if 1
15795   int drop_element = get_next_dropped_element(player);
15796 #else
15797   int drop_element = (player->inventory_size > 0 ?
15798                       player->inventory_element[player->inventory_size - 1] :
15799                       player->inventory_infinite_element != EL_UNDEFINED ?
15800                       player->inventory_infinite_element :
15801                       player->dynabombs_left > 0 ?
15802                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15803                       EL_UNDEFINED);
15804 #endif
15805
15806   player->is_dropping_pressed = TRUE;
15807
15808   /* do not drop an element on top of another element; when holding drop key
15809      pressed without moving, dropped element must move away before the next
15810      element can be dropped (this is especially important if the next element
15811      is dynamite, which can be placed on background for historical reasons) */
15812   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15813     return MP_ACTION;
15814
15815   if (IS_THROWABLE(drop_element))
15816   {
15817     dropx += GET_DX_FROM_DIR(drop_direction);
15818     dropy += GET_DY_FROM_DIR(drop_direction);
15819
15820     if (!IN_LEV_FIELD(dropx, dropy))
15821       return FALSE;
15822   }
15823
15824   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15825   new_element = drop_element;           /* default: no change when dropping */
15826
15827   /* check if player is active, not moving and ready to drop */
15828   if (!player->active || player->MovPos || player->drop_delay > 0)
15829     return FALSE;
15830
15831   /* check if player has anything that can be dropped */
15832   if (new_element == EL_UNDEFINED)
15833     return FALSE;
15834
15835   /* check if drop key was pressed long enough for EM style dynamite */
15836   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15837     return FALSE;
15838
15839   /* check if anything can be dropped at the current position */
15840   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15841     return FALSE;
15842
15843   /* collected custom elements can only be dropped on empty fields */
15844   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15845     return FALSE;
15846
15847   if (old_element != EL_EMPTY)
15848     Back[dropx][dropy] = old_element;   /* store old element on this field */
15849
15850   ResetGfxAnimation(dropx, dropy);
15851   ResetRandomAnimationValue(dropx, dropy);
15852
15853   if (player->inventory_size > 0 ||
15854       player->inventory_infinite_element != EL_UNDEFINED)
15855   {
15856     if (player->inventory_size > 0)
15857     {
15858       player->inventory_size--;
15859
15860       DrawGameDoorValues();
15861
15862       if (new_element == EL_DYNAMITE)
15863         new_element = EL_DYNAMITE_ACTIVE;
15864       else if (new_element == EL_EM_DYNAMITE)
15865         new_element = EL_EM_DYNAMITE_ACTIVE;
15866       else if (new_element == EL_SP_DISK_RED)
15867         new_element = EL_SP_DISK_RED_ACTIVE;
15868     }
15869
15870     Feld[dropx][dropy] = new_element;
15871
15872     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15873       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15874                           el2img(Feld[dropx][dropy]), 0);
15875
15876     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15877
15878     /* needed if previous element just changed to "empty" in the last frame */
15879     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15880
15881     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15882                                player->index_bit, drop_side);
15883     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15884                                         CE_PLAYER_DROPS_X,
15885                                         player->index_bit, drop_side);
15886
15887     TestIfElementTouchesCustomElement(dropx, dropy);
15888   }
15889   else          /* player is dropping a dyna bomb */
15890   {
15891     player->dynabombs_left--;
15892
15893     Feld[dropx][dropy] = new_element;
15894
15895     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15896       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15897                           el2img(Feld[dropx][dropy]), 0);
15898
15899     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15900   }
15901
15902   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15903     InitField_WithBug1(dropx, dropy, FALSE);
15904
15905   new_element = Feld[dropx][dropy];     /* element might have changed */
15906
15907   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15908       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15909   {
15910     int move_direction, nextx, nexty;
15911
15912     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15913       MovDir[dropx][dropy] = drop_direction;
15914
15915     move_direction = MovDir[dropx][dropy];
15916     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15917     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15918
15919     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15920
15921 #if USE_FIX_IMPACT_COLLISION
15922     /* do not cause impact style collision by dropping elements that can fall */
15923     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15924 #else
15925     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15926 #endif
15927   }
15928
15929   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15930   player->is_dropping = TRUE;
15931
15932   player->drop_pressed_delay = 0;
15933   player->is_dropping_pressed = FALSE;
15934
15935   player->drop_x = dropx;
15936   player->drop_y = dropy;
15937
15938   return TRUE;
15939 }
15940
15941 /* ------------------------------------------------------------------------- */
15942 /* game sound playing functions                                              */
15943 /* ------------------------------------------------------------------------- */
15944
15945 static int *loop_sound_frame = NULL;
15946 static int *loop_sound_volume = NULL;
15947
15948 void InitPlayLevelSound()
15949 {
15950   int num_sounds = getSoundListSize();
15951
15952   checked_free(loop_sound_frame);
15953   checked_free(loop_sound_volume);
15954
15955   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15956   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15957 }
15958
15959 static void PlayLevelSound(int x, int y, int nr)
15960 {
15961   int sx = SCREENX(x), sy = SCREENY(y);
15962   int volume, stereo_position;
15963   int max_distance = 8;
15964   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15965
15966   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15967       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15968     return;
15969
15970   if (!IN_LEV_FIELD(x, y) ||
15971       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15972       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15973     return;
15974
15975   volume = SOUND_MAX_VOLUME;
15976
15977   if (!IN_SCR_FIELD(sx, sy))
15978   {
15979     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15980     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15981
15982     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15983   }
15984
15985   stereo_position = (SOUND_MAX_LEFT +
15986                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15987                      (SCR_FIELDX + 2 * max_distance));
15988
15989   if (IS_LOOP_SOUND(nr))
15990   {
15991     /* This assures that quieter loop sounds do not overwrite louder ones,
15992        while restarting sound volume comparison with each new game frame. */
15993
15994     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15995       return;
15996
15997     loop_sound_volume[nr] = volume;
15998     loop_sound_frame[nr] = FrameCounter;
15999   }
16000
16001   PlaySoundExt(nr, volume, stereo_position, type);
16002 }
16003
16004 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16005 {
16006   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16007                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16008                  y < LEVELY(BY1) ? LEVELY(BY1) :
16009                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16010                  sound_action);
16011 }
16012
16013 static void PlayLevelSoundAction(int x, int y, int action)
16014 {
16015   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16016 }
16017
16018 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16019 {
16020   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16021
16022   if (sound_effect != SND_UNDEFINED)
16023     PlayLevelSound(x, y, sound_effect);
16024 }
16025
16026 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16027                                               int action)
16028 {
16029   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16030
16031   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16032     PlayLevelSound(x, y, sound_effect);
16033 }
16034
16035 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16036 {
16037   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16038
16039   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16040     PlayLevelSound(x, y, sound_effect);
16041 }
16042
16043 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16044 {
16045   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16046
16047   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16048     StopSound(sound_effect);
16049 }
16050
16051 static void PlayLevelMusic()
16052 {
16053   if (levelset.music[level_nr] != MUS_UNDEFINED)
16054     PlayMusic(levelset.music[level_nr]);        /* from config file */
16055   else
16056     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16057 }
16058
16059 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16060 {
16061   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16062   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16063   int x = xx - 1 - offset;
16064   int y = yy - 1 - offset;
16065
16066   switch (sample)
16067   {
16068     case SAMPLE_blank:
16069       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16070       break;
16071
16072     case SAMPLE_roll:
16073       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16074       break;
16075
16076     case SAMPLE_stone:
16077       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16078       break;
16079
16080     case SAMPLE_nut:
16081       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16082       break;
16083
16084     case SAMPLE_crack:
16085       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16086       break;
16087
16088     case SAMPLE_bug:
16089       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16090       break;
16091
16092     case SAMPLE_tank:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16094       break;
16095
16096     case SAMPLE_android_clone:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16098       break;
16099
16100     case SAMPLE_android_move:
16101       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16102       break;
16103
16104     case SAMPLE_spring:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16106       break;
16107
16108     case SAMPLE_slurp:
16109       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16110       break;
16111
16112     case SAMPLE_eater:
16113       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16114       break;
16115
16116     case SAMPLE_eater_eat:
16117       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16118       break;
16119
16120     case SAMPLE_alien:
16121       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16122       break;
16123
16124     case SAMPLE_collect:
16125       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16126       break;
16127
16128     case SAMPLE_diamond:
16129       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16130       break;
16131
16132     case SAMPLE_squash:
16133       /* !!! CHECK THIS !!! */
16134 #if 1
16135       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16136 #else
16137       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16138 #endif
16139       break;
16140
16141     case SAMPLE_wonderfall:
16142       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16143       break;
16144
16145     case SAMPLE_drip:
16146       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16147       break;
16148
16149     case SAMPLE_push:
16150       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16151       break;
16152
16153     case SAMPLE_dirt:
16154       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16155       break;
16156
16157     case SAMPLE_acid:
16158       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16159       break;
16160
16161     case SAMPLE_ball:
16162       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16163       break;
16164
16165     case SAMPLE_grow:
16166       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16167       break;
16168
16169     case SAMPLE_wonder:
16170       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16171       break;
16172
16173     case SAMPLE_door:
16174       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16175       break;
16176
16177     case SAMPLE_exit_open:
16178       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16179       break;
16180
16181     case SAMPLE_exit_leave:
16182       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16183       break;
16184
16185     case SAMPLE_dynamite:
16186       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16187       break;
16188
16189     case SAMPLE_tick:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16191       break;
16192
16193     case SAMPLE_press:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16195       break;
16196
16197     case SAMPLE_wheel:
16198       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16199       break;
16200
16201     case SAMPLE_boom:
16202       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16203       break;
16204
16205     case SAMPLE_die:
16206       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16207       break;
16208
16209     case SAMPLE_time:
16210       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16211       break;
16212
16213     default:
16214       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16215       break;
16216   }
16217 }
16218
16219 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16220 {
16221   int element = map_element_SP_to_RND(element_sp);
16222   int action = map_action_SP_to_RND(action_sp);
16223   int offset = (setup.sp_show_border_elements ? 0 : 1);
16224   int x = xx - offset;
16225   int y = yy - offset;
16226
16227 #if 0
16228   printf("::: %d -> %d\n", element_sp, action_sp);
16229 #endif
16230
16231   PlayLevelSoundElementAction(x, y, element, action);
16232 }
16233
16234 #if 0
16235 void ChangeTime(int value)
16236 {
16237   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16238
16239   *time += value;
16240
16241   /* EMC game engine uses value from time counter of RND game engine */
16242   level.native_em_level->lev->time = *time;
16243
16244   DrawGameValue_Time(*time);
16245 }
16246
16247 void RaiseScore(int value)
16248 {
16249   /* EMC game engine and RND game engine have separate score counters */
16250   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16251                 &level.native_em_level->lev->score : &local_player->score);
16252
16253   *score += value;
16254
16255   DrawGameValue_Score(*score);
16256 }
16257 #endif
16258
16259 void RaiseScore(int value)
16260 {
16261   local_player->score += value;
16262
16263 #if 1
16264   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16265
16266   DisplayGameControlValues();
16267 #else
16268   DrawGameValue_Score(local_player->score);
16269 #endif
16270 }
16271
16272 void RaiseScoreElement(int element)
16273 {
16274   switch (element)
16275   {
16276     case EL_EMERALD:
16277     case EL_BD_DIAMOND:
16278     case EL_EMERALD_YELLOW:
16279     case EL_EMERALD_RED:
16280     case EL_EMERALD_PURPLE:
16281     case EL_SP_INFOTRON:
16282       RaiseScore(level.score[SC_EMERALD]);
16283       break;
16284     case EL_DIAMOND:
16285       RaiseScore(level.score[SC_DIAMOND]);
16286       break;
16287     case EL_CRYSTAL:
16288       RaiseScore(level.score[SC_CRYSTAL]);
16289       break;
16290     case EL_PEARL:
16291       RaiseScore(level.score[SC_PEARL]);
16292       break;
16293     case EL_BUG:
16294     case EL_BD_BUTTERFLY:
16295     case EL_SP_ELECTRON:
16296       RaiseScore(level.score[SC_BUG]);
16297       break;
16298     case EL_SPACESHIP:
16299     case EL_BD_FIREFLY:
16300     case EL_SP_SNIKSNAK:
16301       RaiseScore(level.score[SC_SPACESHIP]);
16302       break;
16303     case EL_YAMYAM:
16304     case EL_DARK_YAMYAM:
16305       RaiseScore(level.score[SC_YAMYAM]);
16306       break;
16307     case EL_ROBOT:
16308       RaiseScore(level.score[SC_ROBOT]);
16309       break;
16310     case EL_PACMAN:
16311       RaiseScore(level.score[SC_PACMAN]);
16312       break;
16313     case EL_NUT:
16314       RaiseScore(level.score[SC_NUT]);
16315       break;
16316     case EL_DYNAMITE:
16317     case EL_EM_DYNAMITE:
16318     case EL_SP_DISK_RED:
16319     case EL_DYNABOMB_INCREASE_NUMBER:
16320     case EL_DYNABOMB_INCREASE_SIZE:
16321     case EL_DYNABOMB_INCREASE_POWER:
16322       RaiseScore(level.score[SC_DYNAMITE]);
16323       break;
16324     case EL_SHIELD_NORMAL:
16325     case EL_SHIELD_DEADLY:
16326       RaiseScore(level.score[SC_SHIELD]);
16327       break;
16328     case EL_EXTRA_TIME:
16329       RaiseScore(level.extra_time_score);
16330       break;
16331     case EL_KEY_1:
16332     case EL_KEY_2:
16333     case EL_KEY_3:
16334     case EL_KEY_4:
16335     case EL_EM_KEY_1:
16336     case EL_EM_KEY_2:
16337     case EL_EM_KEY_3:
16338     case EL_EM_KEY_4:
16339     case EL_EMC_KEY_5:
16340     case EL_EMC_KEY_6:
16341     case EL_EMC_KEY_7:
16342     case EL_EMC_KEY_8:
16343     case EL_DC_KEY_WHITE:
16344       RaiseScore(level.score[SC_KEY]);
16345       break;
16346     default:
16347       RaiseScore(element_info[element].collect_score);
16348       break;
16349   }
16350 }
16351
16352 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16353 {
16354   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16355   {
16356 #if defined(NETWORK_AVALIABLE)
16357     if (options.network)
16358       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16359     else
16360 #endif
16361     {
16362       if (quick_quit)
16363       {
16364 #if 1
16365
16366 #if 1
16367         FadeSkipNextFadeIn();
16368 #else
16369         fading = fading_none;
16370 #endif
16371
16372 #else
16373         OpenDoor(DOOR_CLOSE_1);
16374 #endif
16375
16376         game_status = GAME_MODE_MAIN;
16377
16378 #if 1
16379         DrawAndFadeInMainMenu(REDRAW_FIELD);
16380 #else
16381         DrawMainMenu();
16382 #endif
16383       }
16384       else
16385       {
16386 #if 0
16387         FadeOut(REDRAW_FIELD);
16388 #endif
16389
16390         game_status = GAME_MODE_MAIN;
16391
16392         DrawAndFadeInMainMenu(REDRAW_FIELD);
16393       }
16394     }
16395   }
16396   else          /* continue playing the game */
16397   {
16398     if (tape.playing && tape.deactivate_display)
16399       TapeDeactivateDisplayOff(TRUE);
16400
16401     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16402
16403     if (tape.playing && tape.deactivate_display)
16404       TapeDeactivateDisplayOn();
16405   }
16406 }
16407
16408 void RequestQuitGame(boolean ask_if_really_quit)
16409 {
16410   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16411   boolean skip_request = AllPlayersGone || quick_quit;
16412
16413   RequestQuitGameExt(skip_request, quick_quit,
16414                      "Do you really want to quit the game ?");
16415 }
16416
16417
16418 /* ------------------------------------------------------------------------- */
16419 /* random generator functions                                                */
16420 /* ------------------------------------------------------------------------- */
16421
16422 unsigned int InitEngineRandom_RND(long seed)
16423 {
16424   game.num_random_calls = 0;
16425
16426 #if 0
16427   unsigned int rnd_seed = InitEngineRandom(seed);
16428
16429   printf("::: START RND: %d\n", rnd_seed);
16430
16431   return rnd_seed;
16432 #else
16433
16434   return InitEngineRandom(seed);
16435
16436 #endif
16437
16438 }
16439
16440 unsigned int RND(int max)
16441 {
16442   if (max > 0)
16443   {
16444     game.num_random_calls++;
16445
16446     return GetEngineRandom(max);
16447   }
16448
16449   return 0;
16450 }
16451
16452
16453 /* ------------------------------------------------------------------------- */
16454 /* game engine snapshot handling functions                                   */
16455 /* ------------------------------------------------------------------------- */
16456
16457 struct EngineSnapshotInfo
16458 {
16459   /* runtime values for custom element collect score */
16460   int collect_score[NUM_CUSTOM_ELEMENTS];
16461
16462   /* runtime values for group element choice position */
16463   int choice_pos[NUM_GROUP_ELEMENTS];
16464
16465   /* runtime values for belt position animations */
16466   int belt_graphic[4][NUM_BELT_PARTS];
16467   int belt_anim_mode[4][NUM_BELT_PARTS];
16468 };
16469
16470 static struct EngineSnapshotInfo engine_snapshot_rnd;
16471 static char *snapshot_level_identifier = NULL;
16472 static int snapshot_level_nr = -1;
16473
16474 static void SaveEngineSnapshotValues_RND()
16475 {
16476   static int belt_base_active_element[4] =
16477   {
16478     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16479     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16480     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16481     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16482   };
16483   int i, j;
16484
16485   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16486   {
16487     int element = EL_CUSTOM_START + i;
16488
16489     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16490   }
16491
16492   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16493   {
16494     int element = EL_GROUP_START + i;
16495
16496     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16497   }
16498
16499   for (i = 0; i < 4; i++)
16500   {
16501     for (j = 0; j < NUM_BELT_PARTS; j++)
16502     {
16503       int element = belt_base_active_element[i] + j;
16504       int graphic = el2img(element);
16505       int anim_mode = graphic_info[graphic].anim_mode;
16506
16507       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16508       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16509     }
16510   }
16511 }
16512
16513 static void LoadEngineSnapshotValues_RND()
16514 {
16515   unsigned long num_random_calls = game.num_random_calls;
16516   int i, j;
16517
16518   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16519   {
16520     int element = EL_CUSTOM_START + i;
16521
16522     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16523   }
16524
16525   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16526   {
16527     int element = EL_GROUP_START + i;
16528
16529     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16530   }
16531
16532   for (i = 0; i < 4; i++)
16533   {
16534     for (j = 0; j < NUM_BELT_PARTS; j++)
16535     {
16536       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16537       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16538
16539       graphic_info[graphic].anim_mode = anim_mode;
16540     }
16541   }
16542
16543   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16544   {
16545     InitRND(tape.random_seed);
16546     for (i = 0; i < num_random_calls; i++)
16547       RND(1);
16548   }
16549
16550   if (game.num_random_calls != num_random_calls)
16551   {
16552     Error(ERR_INFO, "number of random calls out of sync");
16553     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16554     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16555     Error(ERR_EXIT, "this should not happen -- please debug");
16556   }
16557 }
16558
16559 void SaveEngineSnapshot()
16560 {
16561   /* do not save snapshots from editor */
16562   if (level_editor_test_game)
16563     return;
16564
16565   /* free previous snapshot buffers, if needed */
16566   FreeEngineSnapshotBuffers();
16567
16568   /* copy some special values to a structure better suited for the snapshot */
16569
16570   SaveEngineSnapshotValues_RND();
16571   SaveEngineSnapshotValues_EM();
16572   SaveEngineSnapshotValues_SP();
16573
16574   /* save values stored in special snapshot structure */
16575
16576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16579
16580   /* save further RND engine values */
16581
16582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16585
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16590
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16596
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16600
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16602
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16604
16605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16607
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16626
16627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16629
16630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16633
16634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16636
16637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16642
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16645
16646   /* save level identification information */
16647
16648   setString(&snapshot_level_identifier, leveldir_current->identifier);
16649   snapshot_level_nr = level_nr;
16650
16651 #if 0
16652   ListNode *node = engine_snapshot_list_rnd;
16653   int num_bytes = 0;
16654
16655   while (node != NULL)
16656   {
16657     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16658
16659     node = node->next;
16660   }
16661
16662   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16663 #endif
16664 }
16665
16666 void LoadEngineSnapshot()
16667 {
16668   /* restore generically stored snapshot buffers */
16669
16670   LoadEngineSnapshotBuffers();
16671
16672   /* restore special values from snapshot structure */
16673
16674   LoadEngineSnapshotValues_RND();
16675   LoadEngineSnapshotValues_EM();
16676   LoadEngineSnapshotValues_SP();
16677 }
16678
16679 boolean CheckEngineSnapshot()
16680 {
16681   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16682           snapshot_level_nr == level_nr);
16683 }
16684
16685
16686 /* ---------- new game button stuff ---------------------------------------- */
16687
16688 /* graphic position values for game buttons */
16689 #define GAME_BUTTON_XSIZE       30
16690 #define GAME_BUTTON_YSIZE       30
16691 #define GAME_BUTTON_XPOS        5
16692 #define GAME_BUTTON_YPOS        215
16693 #define SOUND_BUTTON_XPOS       5
16694 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16695
16696 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16697 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16698 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16699 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16700 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16701 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16702
16703 static struct
16704 {
16705   int *x, *y;
16706   int gd_x, gd_y;
16707   int gadget_id;
16708   char *infotext;
16709 } gamebutton_info[NUM_GAME_BUTTONS] =
16710 {
16711 #if 1
16712   {
16713     &game.button.stop.x,        &game.button.stop.y,
16714     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16715     GAME_CTRL_ID_STOP,
16716     "stop game"
16717   },
16718   {
16719     &game.button.pause.x,       &game.button.pause.y,
16720     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16721     GAME_CTRL_ID_PAUSE,
16722     "pause game"
16723   },
16724   {
16725     &game.button.play.x,        &game.button.play.y,
16726     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16727     GAME_CTRL_ID_PLAY,
16728     "play game"
16729   },
16730   {
16731     &game.button.sound_music.x, &game.button.sound_music.y,
16732     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16733     SOUND_CTRL_ID_MUSIC,
16734     "background music on/off"
16735   },
16736   {
16737     &game.button.sound_loops.x, &game.button.sound_loops.y,
16738     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16739     SOUND_CTRL_ID_LOOPS,
16740     "sound loops on/off"
16741   },
16742   {
16743     &game.button.sound_simple.x,&game.button.sound_simple.y,
16744     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16745     SOUND_CTRL_ID_SIMPLE,
16746     "normal sounds on/off"
16747   }
16748 #else
16749   {
16750     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16751     GAME_CTRL_ID_STOP,
16752     "stop game"
16753   },
16754   {
16755     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16756     GAME_CTRL_ID_PAUSE,
16757     "pause game"
16758   },
16759   {
16760     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16761     GAME_CTRL_ID_PLAY,
16762     "play game"
16763   },
16764   {
16765     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16766     SOUND_CTRL_ID_MUSIC,
16767     "background music on/off"
16768   },
16769   {
16770     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16771     SOUND_CTRL_ID_LOOPS,
16772     "sound loops on/off"
16773   },
16774   {
16775     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16776     SOUND_CTRL_ID_SIMPLE,
16777     "normal sounds on/off"
16778   }
16779 #endif
16780 };
16781
16782 void CreateGameButtons()
16783 {
16784   int i;
16785
16786   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16787   {
16788     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16789     struct GadgetInfo *gi;
16790     int button_type;
16791     boolean checked;
16792     unsigned long event_mask;
16793     int x, y;
16794     int gd_xoffset, gd_yoffset;
16795     int gd_x1, gd_x2, gd_y1, gd_y2;
16796     int id = i;
16797
16798     x = DX + *gamebutton_info[i].x;
16799     y = DY + *gamebutton_info[i].y;
16800     gd_xoffset = gamebutton_info[i].gd_x;
16801     gd_yoffset = gamebutton_info[i].gd_y;
16802     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16803     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16804
16805     if (id == GAME_CTRL_ID_STOP ||
16806         id == GAME_CTRL_ID_PAUSE ||
16807         id == GAME_CTRL_ID_PLAY)
16808     {
16809       button_type = GD_TYPE_NORMAL_BUTTON;
16810       checked = FALSE;
16811       event_mask = GD_EVENT_RELEASED;
16812       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16813       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16814     }
16815     else
16816     {
16817       button_type = GD_TYPE_CHECK_BUTTON;
16818       checked =
16819         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16820          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16821          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16822       event_mask = GD_EVENT_PRESSED;
16823       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16824       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16825     }
16826
16827     gi = CreateGadget(GDI_CUSTOM_ID, id,
16828                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16829 #if 1
16830                       GDI_X, x,
16831                       GDI_Y, y,
16832 #else
16833                       GDI_X, DX + gd_xoffset,
16834                       GDI_Y, DY + gd_yoffset,
16835 #endif
16836                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16837                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16838                       GDI_TYPE, button_type,
16839                       GDI_STATE, GD_BUTTON_UNPRESSED,
16840                       GDI_CHECKED, checked,
16841                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16842                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16843                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16844                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16845                       GDI_DIRECT_DRAW, FALSE,
16846                       GDI_EVENT_MASK, event_mask,
16847                       GDI_CALLBACK_ACTION, HandleGameButtons,
16848                       GDI_END);
16849
16850     if (gi == NULL)
16851       Error(ERR_EXIT, "cannot create gadget");
16852
16853     game_gadget[id] = gi;
16854   }
16855 }
16856
16857 void FreeGameButtons()
16858 {
16859   int i;
16860
16861   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862     FreeGadget(game_gadget[i]);
16863 }
16864
16865 static void MapGameButtons()
16866 {
16867   int i;
16868
16869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870     MapGadget(game_gadget[i]);
16871 }
16872
16873 void UnmapGameButtons()
16874 {
16875   int i;
16876
16877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878     UnmapGadget(game_gadget[i]);
16879 }
16880
16881 void RedrawGameButtons()
16882 {
16883   int i;
16884
16885   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886     RedrawGadget(game_gadget[i]);
16887 }
16888
16889 static void HandleGameButtonsExt(int id)
16890 {
16891   if (game_status != GAME_MODE_PLAYING)
16892     return;
16893
16894   switch (id)
16895   {
16896     case GAME_CTRL_ID_STOP:
16897       if (tape.playing)
16898         TapeStop();
16899       else
16900         RequestQuitGame(TRUE);
16901       break;
16902
16903     case GAME_CTRL_ID_PAUSE:
16904       if (options.network)
16905       {
16906 #if defined(NETWORK_AVALIABLE)
16907         if (tape.pausing)
16908           SendToServer_ContinuePlaying();
16909         else
16910           SendToServer_PausePlaying();
16911 #endif
16912       }
16913       else
16914         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16915       break;
16916
16917     case GAME_CTRL_ID_PLAY:
16918       if (tape.pausing)
16919       {
16920 #if defined(NETWORK_AVALIABLE)
16921         if (options.network)
16922           SendToServer_ContinuePlaying();
16923         else
16924 #endif
16925         {
16926           tape.pausing = FALSE;
16927           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16928         }
16929       }
16930       break;
16931
16932     case SOUND_CTRL_ID_MUSIC:
16933       if (setup.sound_music)
16934       { 
16935         setup.sound_music = FALSE;
16936
16937         FadeMusic();
16938       }
16939       else if (audio.music_available)
16940       { 
16941         setup.sound = setup.sound_music = TRUE;
16942
16943         SetAudioMode(setup.sound);
16944
16945         PlayLevelMusic();
16946       }
16947       break;
16948
16949     case SOUND_CTRL_ID_LOOPS:
16950       if (setup.sound_loops)
16951         setup.sound_loops = FALSE;
16952       else if (audio.loops_available)
16953       {
16954         setup.sound = setup.sound_loops = TRUE;
16955
16956         SetAudioMode(setup.sound);
16957       }
16958       break;
16959
16960     case SOUND_CTRL_ID_SIMPLE:
16961       if (setup.sound_simple)
16962         setup.sound_simple = FALSE;
16963       else if (audio.sound_available)
16964       {
16965         setup.sound = setup.sound_simple = TRUE;
16966
16967         SetAudioMode(setup.sound);
16968       }
16969       break;
16970
16971     default:
16972       break;
16973   }
16974 }
16975
16976 static void HandleGameButtons(struct GadgetInfo *gi)
16977 {
16978   HandleGameButtonsExt(gi->custom_id);
16979 }
16980
16981 void HandleSoundButtonKeys(Key key)
16982 {
16983 #if 1
16984   if (key == setup.shortcut.sound_simple)
16985     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16986   else if (key == setup.shortcut.sound_loops)
16987     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16988   else if (key == setup.shortcut.sound_music)
16989     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16990 #else
16991   if (key == setup.shortcut.sound_simple)
16992     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16993   else if (key == setup.shortcut.sound_loops)
16994     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16995   else if (key == setup.shortcut.sound_music)
16996     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16997 #endif
16998 }