rnd-20100407-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_FRAME                        35
198 #define GAME_PANEL_SHIELD_NORMAL                36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
200 #define GAME_PANEL_SHIELD_DEADLY                38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
202 #define GAME_PANEL_EXIT                         40
203 #define GAME_PANEL_EMC_MAGIC_BALL               41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
205 #define GAME_PANEL_LIGHT_SWITCH                 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
207 #define GAME_PANEL_TIMEGATE_SWITCH              45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
209 #define GAME_PANEL_SWITCHGATE_SWITCH            47
210 #define GAME_PANEL_EMC_LENSES                   48
211 #define GAME_PANEL_EMC_LENSES_TIME              49
212 #define GAME_PANEL_EMC_MAGNIFIER                50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
214 #define GAME_PANEL_BALLOON_SWITCH               52
215 #define GAME_PANEL_DYNABOMB_NUMBER              53
216 #define GAME_PANEL_DYNABOMB_SIZE                54
217 #define GAME_PANEL_DYNABOMB_POWER               55
218 #define GAME_PANEL_PENGUINS                     56
219 #define GAME_PANEL_SOKOBAN_OBJECTS              57
220 #define GAME_PANEL_SOKOBAN_FIELDS               58
221 #define GAME_PANEL_ROBOT_WHEEL                  59
222 #define GAME_PANEL_CONVEYOR_BELT_1              60
223 #define GAME_PANEL_CONVEYOR_BELT_2              61
224 #define GAME_PANEL_CONVEYOR_BELT_3              62
225 #define GAME_PANEL_CONVEYOR_BELT_4              63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
230 #define GAME_PANEL_MAGIC_WALL                   68
231 #define GAME_PANEL_MAGIC_WALL_TIME              69
232 #define GAME_PANEL_GRAVITY_STATE                70
233 #define GAME_PANEL_GRAPHIC_1                    71
234 #define GAME_PANEL_GRAPHIC_2                    72
235 #define GAME_PANEL_GRAPHIC_3                    73
236 #define GAME_PANEL_GRAPHIC_4                    74
237 #define GAME_PANEL_GRAPHIC_5                    75
238 #define GAME_PANEL_GRAPHIC_6                    76
239 #define GAME_PANEL_GRAPHIC_7                    77
240 #define GAME_PANEL_GRAPHIC_8                    78
241 #define GAME_PANEL_ELEMENT_1                    79
242 #define GAME_PANEL_ELEMENT_2                    80
243 #define GAME_PANEL_ELEMENT_3                    81
244 #define GAME_PANEL_ELEMENT_4                    82
245 #define GAME_PANEL_ELEMENT_5                    83
246 #define GAME_PANEL_ELEMENT_6                    84
247 #define GAME_PANEL_ELEMENT_7                    85
248 #define GAME_PANEL_ELEMENT_8                    86
249 #define GAME_PANEL_ELEMENT_COUNT_1              87
250 #define GAME_PANEL_ELEMENT_COUNT_2              88
251 #define GAME_PANEL_ELEMENT_COUNT_3              89
252 #define GAME_PANEL_ELEMENT_COUNT_4              90
253 #define GAME_PANEL_ELEMENT_COUNT_5              91
254 #define GAME_PANEL_ELEMENT_COUNT_6              92
255 #define GAME_PANEL_ELEMENT_COUNT_7              93
256 #define GAME_PANEL_ELEMENT_COUNT_8              94
257 #define GAME_PANEL_CE_SCORE_1                   95
258 #define GAME_PANEL_CE_SCORE_2                   96
259 #define GAME_PANEL_CE_SCORE_3                   97
260 #define GAME_PANEL_CE_SCORE_4                   98
261 #define GAME_PANEL_CE_SCORE_5                   99
262 #define GAME_PANEL_CE_SCORE_6                   100
263 #define GAME_PANEL_CE_SCORE_7                   101
264 #define GAME_PANEL_CE_SCORE_8                   102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
273 #define GAME_PANEL_PLAYER_NAME                  111
274 #define GAME_PANEL_LEVEL_NAME                   112
275 #define GAME_PANEL_LEVEL_AUTHOR                 113
276
277 #define NUM_GAME_PANEL_CONTROLS                 114
278
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289   int nr;
290
291   struct TextPosInfo *pos;
292   int type;
293
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING      3
884 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION   2
886 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF  -1
890 #define INITIAL_MOVE_DELAY_ON   0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED    32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED   4
896 #define MOVE_DELAY_MAX_SPEED    1
897
898 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN       (1)
907 #define MOVE_STEPSIZE_MAX       (TILEX)
908
909 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911
912 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
915                                  RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
917                                  RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
919                                  RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
921                                     (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
923                                  RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
926                                  RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
928                                  RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
932          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
935         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
936          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
937          (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p)                                         \
940         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
943         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
944          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
945          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
946          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
947          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
948          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
949          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
950          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
951          (e))
952
953 #define CAN_GROW_INTO(e)                                                \
954         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
957                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
968                                         (CAN_MOVE_INTO_ACID(e) &&       \
969                                          Feld[x][y] == EL_ACID) ||      \
970                                         (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
973                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
974                                         (condition) ||                  \
975                                         (CAN_MOVE_INTO_ACID(e) &&       \
976                                          Feld[x][y] == EL_ACID) ||      \
977                                         (DONT_COLLIDE_WITH(e) &&        \
978                                          IS_PLAYER(x, y) &&             \
979                                          !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
991         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1012                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1019         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1022         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1025         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1026                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1031                 (!IS_PLAYER(x, y) &&                                    \
1032                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1035         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP               0
1047 #define GAME_CTRL_ID_PAUSE              1
1048 #define GAME_CTRL_ID_PLAY               2
1049 #define SOUND_CTRL_ID_MUSIC             3
1050 #define SOUND_CTRL_ID_LOOPS             4
1051 #define SOUND_CTRL_ID_SIMPLE            5
1052
1053 #define NUM_GAME_BUTTONS                6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)                             \
1105         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1107         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1109         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1156 {                                                                       \
1157   if (recursion_loop_detected)                                          \
1158     return (rc);                                                        \
1159                                                                         \
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1161   {                                                                     \
1162     recursion_loop_detected = TRUE;                                     \
1163     recursion_loop_element = (e);                                       \
1164   }                                                                     \
1165                                                                         \
1166   recursion_loop_depth++;                                               \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END()                                  \
1170 {                                                                       \
1171   recursion_loop_depth--;                                               \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,                  0, 0 },
1484   { EL_BALLOON,                 0, 0 },
1485
1486   { EL_SOKOBAN_OBJECT,          2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1488   { EL_SATELLITE,               2, 0 },
1489   { EL_SP_DISK_YELLOW,          2, 0 },
1490
1491   { EL_UNDEFINED,               0, 0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,             2 },
1502   { EL_AMOEBA_DROPPING,         2 },
1503   { EL_QUICKSAND_FILLING,       1 },
1504   { EL_QUICKSAND_EMPTYING,      1 },
1505   { EL_QUICKSAND_FAST_FILLING,  2 },
1506   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507   { EL_MAGIC_WALL_FILLING,      2 },
1508   { EL_MAGIC_WALL_EMPTYING,     2 },
1509   { EL_BD_MAGIC_WALL_FILLING,   2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1511   { EL_DC_MAGIC_WALL_FILLING,   2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1513
1514   { EL_UNDEFINED,               0 },
1515 };
1516
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,                 1 },
1525   { EL_BD_DIAMOND,              1 },
1526   { EL_EMERALD_YELLOW,          1 },
1527   { EL_EMERALD_RED,             1 },
1528   { EL_EMERALD_PURPLE,          1 },
1529   { EL_DIAMOND,                 3 },
1530   { EL_SP_INFOTRON,             1 },
1531   { EL_PEARL,                   5 },
1532   { EL_CRYSTAL,                 8 },
1533
1534   { EL_UNDEFINED,               0 },
1535 };
1536
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1555
1556   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1558   { EL_SP_PORT_UP,                                                   MV_DOWN },
1559   { EL_SP_PORT_DOWN,                                         MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1575
1576   { EL_UNDEFINED,                       MV_NONE                              }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1584                                  IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1595                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1596                                      (y) += playfield_scan_delta_y)     \
1597                                 for ((x) = playfield_scan_start_x;      \
1598                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1599                                      (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609         EL_DYNAMITE;
1610 }
1611 #endif
1612
1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632
1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638   InitPlayfieldScanModeVars();
1639 }
1640
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648
1649   return TILEX / move_stepsize;
1650 }
1651
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653                                boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670     player->move_delay_value_next = -1;
1671
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675
1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697
1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710                                     group->choice_mode, 0,
1711                                     group->choice_pos);
1712
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715
1716     group->choice_pos++;
1717
1718     element = group->element_resolved[element_pos];
1719   }
1720
1721   return element;
1722 }
1723
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732         Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734         return;
1735       }
1736       else
1737       {
1738         stored_player[0].initial_element = element;
1739         stored_player[0].use_murphy = TRUE;
1740
1741         if (!level.use_artwork_element[0])
1742           stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753
1754     player->present = TRUE;
1755
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757                                 level.sp_block_last_field :
1758                                 level.block_last_field);
1759
1760     /* ---------- initialize player's last field block delay --------------- */
1761
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779         StorePlayer[jx][jy] = 0;
1780
1781       StorePlayer[x][y] = Feld[x][y];
1782
1783       if (options.debug)
1784       {
1785         printf("Player %d activated.\n", player->element_nr);
1786         printf("[Local player is %d and currently %s.]\n",
1787                local_player->element_nr,
1788                local_player->active ? "active" : "not active");
1789       }
1790     }
1791
1792     Feld[x][y] = EL_EMPTY;
1793
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809
1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899         Feld[x][y] = EL_AMOEBA_GROWING;
1900         Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1949         {
1950           game.belt_dir[belt_nr] = belt_dir;
1951           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952         }
1953         else    /* more than one switch -- set it like the first switch */
1954         {
1955           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956         }
1957       }
1958       break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1962       if (init_game)
1963         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1967       if (init_game)
1968         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981           game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026         if (CAN_MOVE(element))
2027           InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036         Feld[x][y] = GetElementFromGroupElement(element);
2037
2038         InitField(x, y, init_game);
2039       }
2040
2041       break;
2042   }
2043
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061
2062   InitField(x, y, init_game);
2063
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078
2079 #if 1
2080
2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085                           EL_EM_KEY_1 : EL_KEY_1);
2086
2087   return key_base_element + key_nr;
2088 }
2089
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093           player->inventory_element[player->inventory_size - 1] :
2094           player->inventory_infinite_element != EL_UNDEFINED ?
2095           player->inventory_infinite_element :
2096           player->dynabombs_left > 0 ?
2097           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098           EL_UNDEFINED);
2099 }
2100
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112     return (player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             player->inventory_infinite_element != EL_UNDEFINED ?
2115             player->inventory_infinite_element :
2116             player->dynabombs_left >= min_dynabombs_left ?
2117             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118             EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127             player->inventory_infinite_element :
2128             player->dynabombs_left >= min_dynabombs_left ?
2129             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130             player->inventory_size >= min_inventory_size ?
2131             player->inventory_element[inventory_pos] :
2132             EL_UNDEFINED);
2133   }
2134 }
2135
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146
2147   return compare_result;
2148 }
2149
2150 void InitGameControlValues()
2151 {
2152   int i;
2153
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226   int time = (local_player->LevelSolved ?
2227               local_player->LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->time_played :
2232               level.time == 0 ? TimePlayed : TimeLeft);
2233   int score = (local_player->LevelSolved ?
2234                local_player->LevelSolved_CountingScore :
2235                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                level.native_em_level->lev->score :
2237                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                level.native_sp_level->game_sp->score :
2239                local_player->score);
2240   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241               level.native_em_level->lev->required :
2242               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243               level.native_sp_level->game_sp->infotrons_still_needed :
2244               local_player->gems_still_needed);
2245   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                      level.native_em_level->lev->required > 0 :
2247                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249                      local_player->gems_still_needed > 0 ||
2250                      local_player->sokobanfields_still_needed > 0 ||
2251                      local_player->lights_still_needed > 0);
2252
2253   UpdatePlayfieldElementCount();
2254
2255   /* update game panel control values */
2256
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       /* only one player in Supaplex game engine */
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289           level.native_em_level->ply[i]->dynamite;
2290       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292           level.native_sp_level->game_sp->red_disk_count;
2293       else
2294         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295           stored_player[i].inventory_size;
2296
2297       if (stored_player[i].num_white_keys > 0)
2298         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299           EL_DC_KEY_WHITE;
2300
2301       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302         stored_player[i].num_white_keys;
2303     }
2304   }
2305   else
2306   {
2307     int player_nr = game.centered_player_nr;
2308
2309     for (k = 0; k < MAX_NUM_KEYS; k++)
2310     {
2311       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312       {
2313         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315             get_key_element_from_nr(k);
2316       }
2317       else if (stored_player[player_nr].key[k])
2318         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319           get_key_element_from_nr(k);
2320     }
2321
2322     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         level.native_em_level->ply[player_nr]->dynamite;
2325     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327         level.native_sp_level->game_sp->red_disk_count;
2328     else
2329       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330         stored_player[player_nr].inventory_size;
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340   {
2341     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342       get_inventory_element_from_pos(local_player, i);
2343     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344       get_inventory_element_from_pos(local_player, -i - 1);
2345   }
2346
2347   game_panel_controls[GAME_PANEL_SCORE].value = score;
2348   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349
2350   game_panel_controls[GAME_PANEL_TIME].value = time;
2351
2352   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355
2356   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360      EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362     local_player->shield_normal_time_left;
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365      EL_EMPTY);
2366   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367     local_player->shield_deadly_time_left;
2368
2369   game_panel_controls[GAME_PANEL_EXIT].value =
2370     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376      EL_EMC_MAGIC_BALL_SWITCH);
2377
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381     game.light_time_left;
2382
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386     game.timegate_time_left;
2387
2388   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390
2391   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394     game.lenses_time_left;
2395
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399     game.magnify_time_left;
2400
2401   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2403      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2405      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2406      EL_BALLOON_SWITCH_NONE);
2407
2408   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409     local_player->dynabomb_count;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411     local_player->dynabomb_size;
2412   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414
2415   game_panel_controls[GAME_PANEL_PENGUINS].value =
2416     local_player->friends_still_needed;
2417
2418   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419     local_player->sokobanfields_still_needed;
2420   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421     local_player->sokobanfields_still_needed;
2422
2423   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425
2426   for (i = 0; i < NUM_BELTS; i++)
2427   {
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433   }
2434
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438     game.magic_wall_time_left;
2439
2440 #if USE_PLAYER_GRAVITY
2441   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442     local_player->gravity;
2443 #else
2444   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446
2447   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449
2450   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453        game.panel.element[i].id : EL_UNDEFINED);
2454
2455   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458        element_info[game.panel.element_count[i].id].element_count : 0);
2459
2460   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463        element_info[game.panel.ce_score[i].id].collect_score : 0);
2464
2465   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468        element_info[game.panel.ce_score_element[i].id].collect_score :
2469        EL_UNDEFINED);
2470
2471   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474
2475   /* update game panel control frames */
2476
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481     if (gpc->type == TYPE_ELEMENT)
2482     {
2483       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484       {
2485         int last_anim_random_frame = gfx.anim_random_frame;
2486         int element = gpc->value;
2487         int graphic = el2panelimg(element);
2488
2489         if (gpc->value != gpc->last_value)
2490         {
2491           gpc->gfx_frame = 0;
2492           gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494         else
2495         {
2496           gpc->gfx_frame++;
2497
2498           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500             gpc->gfx_random = INIT_GFX_RANDOM();
2501         }
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = gpc->gfx_random;
2505
2506         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507           gpc->gfx_frame = element_info[element].collect_score;
2508
2509         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510                                               gpc->gfx_frame);
2511
2512         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513           gfx.anim_random_frame = last_anim_random_frame;
2514       }
2515     }
2516   }
2517 }
2518
2519 void DisplayGameControlValues()
2520 {
2521   boolean redraw_panel = FALSE;
2522   int i;
2523
2524   for (i = 0; game_panel_controls[i].nr != -1; i++)
2525   {
2526     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527
2528     if (PANEL_DEACTIVATED(gpc->pos))
2529       continue;
2530
2531     if (gpc->value == gpc->last_value &&
2532         gpc->frame == gpc->last_frame)
2533       continue;
2534
2535     redraw_panel = TRUE;
2536   }
2537
2538   if (!redraw_panel)
2539     return;
2540
2541   /* copy default game door content to main double buffer */
2542   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2543              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2544
2545   /* redraw game control buttons */
2546 #if 1
2547   RedrawGameButtons();
2548 #else
2549   UnmapGameButtons();
2550   MapGameButtons();
2551 #endif
2552
2553   game_status = GAME_MODE_PSEUDO_PANEL;
2554
2555 #if 1
2556   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2557 #else
2558   for (i = 0; game_panel_controls[i].nr != -1; i++)
2559 #endif
2560   {
2561 #if 1
2562     int nr = game_panel_order[i].nr;
2563     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2564 #else
2565     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2566     int nr = gpc->nr;
2567 #endif
2568     struct TextPosInfo *pos = gpc->pos;
2569     int type = gpc->type;
2570     int value = gpc->value;
2571     int frame = gpc->frame;
2572 #if 0
2573     int last_value = gpc->last_value;
2574     int last_frame = gpc->last_frame;
2575 #endif
2576     int size = pos->size;
2577     int font = pos->font;
2578     boolean draw_masked = pos->draw_masked;
2579     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2580
2581     if (PANEL_DEACTIVATED(pos))
2582       continue;
2583
2584 #if 0
2585     if (value == last_value && frame == last_frame)
2586       continue;
2587 #endif
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592 #if 0
2593     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2594 #endif
2595
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599           nr == GAME_PANEL_TIME)
2600       {
2601         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602
2603         if (use_dynamic_size)           /* use dynamic number of digits */
2604         {
2605           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607           int size2 = size1 + 1;
2608           int font1 = pos->font;
2609           int font2 = pos->font_alt;
2610
2611           size = (value < value_change ? size1 : size2);
2612           font = (value < value_change ? font1 : font2);
2613
2614 #if 0
2615           /* clear background if value just changed its size (dynamic digits) */
2616           if ((last_value < value_change) != (value < value_change))
2617           {
2618             int width1 = size1 * getFontWidth(font1);
2619             int width2 = size2 * getFontWidth(font2);
2620             int max_width = MAX(width1, width2);
2621             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2622
2623             pos->width = max_width;
2624
2625             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2626                                        max_width, max_height);
2627           }
2628 #endif
2629         }
2630       }
2631
2632 #if 1
2633       /* correct text size if "digits" is zero or less */
2634       if (size <= 0)
2635         size = strlen(int2str(value, size));
2636
2637       /* dynamically correct text alignment */
2638       pos->width = size * getFontWidth(font);
2639 #endif
2640
2641       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642                   int2str(value, size), font, mask_mode);
2643     }
2644     else if (type == TYPE_ELEMENT)
2645     {
2646       int element, graphic;
2647       Bitmap *src_bitmap;
2648       int src_x, src_y;
2649       int width, height;
2650       int dst_x = PANEL_XPOS(pos);
2651       int dst_y = PANEL_YPOS(pos);
2652
2653 #if 1
2654       if (value != EL_UNDEFINED && value != EL_EMPTY)
2655       {
2656         element = value;
2657         graphic = el2panelimg(value);
2658
2659         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2660
2661 #if 1
2662         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2663           size = TILESIZE;
2664 #endif
2665
2666         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2667                               &src_x, &src_y);
2668
2669         width  = graphic_info[graphic].width  * size / TILESIZE;
2670         height = graphic_info[graphic].height * size / TILESIZE;
2671
2672         if (draw_masked)
2673         {
2674           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2675                         dst_x - src_x, dst_y - src_y);
2676           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2677                            dst_x, dst_y);
2678         }
2679         else
2680         {
2681           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2682                      dst_x, dst_y);
2683         }
2684       }
2685 #else
2686       if (value == EL_UNDEFINED || value == EL_EMPTY)
2687       {
2688         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2689         graphic = el2panelimg(element);
2690
2691         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2692         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2693         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2694       }
2695       else
2696       {
2697         element = value;
2698         graphic = el2panelimg(value);
2699
2700         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2701       }
2702
2703       width  = graphic_info[graphic].width  * size / TILESIZE;
2704       height = graphic_info[graphic].height * size / TILESIZE;
2705
2706       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2707 #endif
2708     }
2709     else if (type == TYPE_STRING)
2710     {
2711       boolean active = (value != 0);
2712       char *state_normal = "off";
2713       char *state_active = "on";
2714       char *state = (active ? state_active : state_normal);
2715       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2716                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2717                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2718                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2719
2720       if (nr == GAME_PANEL_GRAVITY_STATE)
2721       {
2722         int font1 = pos->font;          /* (used for normal state) */
2723         int font2 = pos->font_alt;      /* (used for active state) */
2724 #if 0
2725         int size1 = strlen(state_normal);
2726         int size2 = strlen(state_active);
2727         int width1 = size1 * getFontWidth(font1);
2728         int width2 = size2 * getFontWidth(font2);
2729         int max_width = MAX(width1, width2);
2730         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2731
2732         pos->width = max_width;
2733
2734         /* clear background for values that may have changed its size */
2735         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736                                    max_width, max_height);
2737 #endif
2738
2739         font = (active ? font2 : font1);
2740       }
2741
2742       if (s != NULL)
2743       {
2744         char *s_cut;
2745
2746 #if 1
2747         if (size <= 0)
2748         {
2749           /* don't truncate output if "chars" is zero or less */
2750           size = strlen(s);
2751
2752           /* dynamically correct text alignment */
2753           pos->width = size * getFontWidth(font);
2754         }
2755 #endif
2756
2757         s_cut = getStringCopyN(s, size);
2758
2759         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2760                     s_cut, font, mask_mode);
2761
2762         free(s_cut);
2763       }
2764     }
2765
2766     redraw_mask |= REDRAW_DOOR_1;
2767   }
2768
2769   game_status = GAME_MODE_PLAYING;
2770 }
2771
2772 void UpdateAndDisplayGameControlValues()
2773 {
2774   if (tape.warp_forward)
2775     return;
2776
2777   UpdateGameControlValues();
2778   DisplayGameControlValues();
2779 }
2780
2781 void DrawGameValue_Emeralds(int value)
2782 {
2783   struct TextPosInfo *pos = &game.panel.gems;
2784 #if 1
2785   int font_nr = pos->font;
2786 #else
2787   int font_nr = FONT_TEXT_2;
2788 #endif
2789   int font_width = getFontWidth(font_nr);
2790   int chars = pos->size;
2791
2792 #if 1
2793   return;       /* !!! USE NEW STUFF !!! */
2794 #endif
2795
2796   if (PANEL_DEACTIVATED(pos))
2797     return;
2798
2799   pos->width = chars * font_width;
2800
2801   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2802 }
2803
2804 void DrawGameValue_Dynamite(int value)
2805 {
2806   struct TextPosInfo *pos = &game.panel.inventory_count;
2807 #if 1
2808   int font_nr = pos->font;
2809 #else
2810   int font_nr = FONT_TEXT_2;
2811 #endif
2812   int font_width = getFontWidth(font_nr);
2813   int chars = pos->size;
2814
2815 #if 1
2816   return;       /* !!! USE NEW STUFF !!! */
2817 #endif
2818
2819   if (PANEL_DEACTIVATED(pos))
2820     return;
2821
2822   pos->width = chars * font_width;
2823
2824   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2825 }
2826
2827 void DrawGameValue_Score(int value)
2828 {
2829   struct TextPosInfo *pos = &game.panel.score;
2830 #if 1
2831   int font_nr = pos->font;
2832 #else
2833   int font_nr = FONT_TEXT_2;
2834 #endif
2835   int font_width = getFontWidth(font_nr);
2836   int chars = pos->size;
2837
2838 #if 1
2839   return;       /* !!! USE NEW STUFF !!! */
2840 #endif
2841
2842   if (PANEL_DEACTIVATED(pos))
2843     return;
2844
2845   pos->width = chars * font_width;
2846
2847   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2848 }
2849
2850 void DrawGameValue_Time(int value)
2851 {
2852   struct TextPosInfo *pos = &game.panel.time;
2853   static int last_value = -1;
2854   int chars1 = 3;
2855   int chars2 = 4;
2856   int chars = pos->size;
2857 #if 1
2858   int font1_nr = pos->font;
2859   int font2_nr = pos->font_alt;
2860 #else
2861   int font1_nr = FONT_TEXT_2;
2862   int font2_nr = FONT_TEXT_1;
2863 #endif
2864   int font_nr = font1_nr;
2865   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866
2867 #if 1
2868   return;       /* !!! USE NEW STUFF !!! */
2869 #endif
2870
2871   if (PANEL_DEACTIVATED(pos))
2872     return;
2873
2874   if (use_dynamic_chars)                /* use dynamic number of chars */
2875   {
2876     chars   = (value < 1000 ? chars1   : chars2);
2877     font_nr = (value < 1000 ? font1_nr : font2_nr);
2878   }
2879
2880   /* clear background if value just changed its size (dynamic chars only) */
2881   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2882   {
2883     int width1 = chars1 * getFontWidth(font1_nr);
2884     int width2 = chars2 * getFontWidth(font2_nr);
2885     int max_width = MAX(width1, width2);
2886     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2887
2888     pos->width = max_width;
2889
2890     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2891                                max_width, max_height);
2892   }
2893
2894   pos->width = chars * getFontWidth(font_nr);
2895
2896   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2897
2898   last_value = value;
2899 }
2900
2901 void DrawGameValue_Level(int value)
2902 {
2903   struct TextPosInfo *pos = &game.panel.level_number;
2904   int chars1 = 2;
2905   int chars2 = 3;
2906   int chars = pos->size;
2907 #if 1
2908   int font1_nr = pos->font;
2909   int font2_nr = pos->font_alt;
2910 #else
2911   int font1_nr = FONT_TEXT_2;
2912   int font2_nr = FONT_TEXT_1;
2913 #endif
2914   int font_nr = font1_nr;
2915   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2916
2917 #if 1
2918   return;       /* !!! USE NEW STUFF !!! */
2919 #endif
2920
2921   if (PANEL_DEACTIVATED(pos))
2922     return;
2923
2924   if (use_dynamic_chars)                /* use dynamic number of chars */
2925   {
2926     chars   = (level_nr < 100 ? chars1   : chars2);
2927     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2928   }
2929
2930   pos->width = chars * getFontWidth(font_nr);
2931
2932   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2933 }
2934
2935 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2936 {
2937 #if 0
2938   struct TextPosInfo *pos = &game.panel.keys;
2939 #endif
2940 #if 0
2941   int base_key_graphic = EL_KEY_1;
2942 #endif
2943   int i;
2944
2945 #if 1
2946   return;       /* !!! USE NEW STUFF !!! */
2947 #endif
2948
2949 #if 0
2950   if (PANEL_DEACTIVATED(pos))
2951     return;
2952 #endif
2953
2954 #if 0
2955   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2956     base_key_graphic = EL_EM_KEY_1;
2957 #endif
2958
2959 #if 0
2960   pos->width = 4 * MINI_TILEX;
2961 #endif
2962
2963 #if 1
2964   for (i = 0; i < MAX_NUM_KEYS; i++)
2965 #else
2966   /* currently only 4 of 8 possible keys are displayed */
2967   for (i = 0; i < STD_NUM_KEYS; i++)
2968 #endif
2969   {
2970 #if 1
2971     struct TextPosInfo *pos = &game.panel.key[i];
2972 #endif
2973     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2974     int src_y = DOOR_GFX_PAGEY1 + 123;
2975 #if 1
2976     int dst_x = PANEL_XPOS(pos);
2977     int dst_y = PANEL_YPOS(pos);
2978 #else
2979     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2980     int dst_y = PANEL_YPOS(pos);
2981 #endif
2982
2983 #if 1
2984     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2985                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2986                    EL_KEY_1) + i;
2987     int graphic = el2edimg(element);
2988 #endif
2989
2990 #if 1
2991     if (PANEL_DEACTIVATED(pos))
2992       continue;
2993 #endif
2994
2995 #if 0
2996     /* masked blit with tiles from half-size scaled bitmap does not work yet
2997        (no mask bitmap created for these sizes after loading and scaling) --
2998        solution: load without creating mask, scale, then create final mask */
2999
3000     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3001                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3002
3003     if (key[i])
3004     {
3005 #if 0
3006       int graphic = el2edimg(base_key_graphic + i);
3007 #endif
3008       Bitmap *src_bitmap;
3009       int src_x, src_y;
3010
3011       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3012
3013       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3014                     dst_x - src_x, dst_y - src_y);
3015       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3016                        dst_x, dst_y);
3017     }
3018 #else
3019 #if 1
3020     if (key[i])
3021       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3022     else
3023       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3024                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3025 #else
3026     if (key[i])
3027       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3028     else
3029       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3030                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3031 #endif
3032 #endif
3033   }
3034 }
3035
3036 #else
3037
3038 void DrawGameValue_Emeralds(int value)
3039 {
3040   int font_nr = FONT_TEXT_2;
3041   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3042
3043   if (PANEL_DEACTIVATED(game.panel.gems))
3044     return;
3045
3046   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3047 }
3048
3049 void DrawGameValue_Dynamite(int value)
3050 {
3051   int font_nr = FONT_TEXT_2;
3052   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3053
3054   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3055     return;
3056
3057   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3058 }
3059
3060 void DrawGameValue_Score(int value)
3061 {
3062   int font_nr = FONT_TEXT_2;
3063   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3064
3065   if (PANEL_DEACTIVATED(game.panel.score))
3066     return;
3067
3068   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3069 }
3070
3071 void DrawGameValue_Time(int value)
3072 {
3073   int font1_nr = FONT_TEXT_2;
3074 #if 1
3075   int font2_nr = FONT_TEXT_1;
3076 #else
3077   int font2_nr = FONT_LEVEL_NUMBER;
3078 #endif
3079   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3080   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3081
3082   if (PANEL_DEACTIVATED(game.panel.time))
3083     return;
3084
3085   /* clear background if value just changed its size */
3086   if (value == 999 || value == 1000)
3087     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3088
3089   if (value < 1000)
3090     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3091   else
3092     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3093 }
3094
3095 void DrawGameValue_Level(int value)
3096 {
3097   int font1_nr = FONT_TEXT_2;
3098 #if 1
3099   int font2_nr = FONT_TEXT_1;
3100 #else
3101   int font2_nr = FONT_LEVEL_NUMBER;
3102 #endif
3103
3104   if (PANEL_DEACTIVATED(game.panel.level))
3105     return;
3106
3107   if (level_nr < 100)
3108     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3109   else
3110     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3111 }
3112
3113 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3114 {
3115   int base_key_graphic = EL_KEY_1;
3116   int i;
3117
3118   if (PANEL_DEACTIVATED(game.panel.keys))
3119     return;
3120
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3122     base_key_graphic = EL_EM_KEY_1;
3123
3124   /* currently only 4 of 8 possible keys are displayed */
3125   for (i = 0; i < STD_NUM_KEYS; i++)
3126   {
3127     int x = XX_KEYS + i * MINI_TILEX;
3128     int y = YY_KEYS;
3129
3130     if (key[i])
3131       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3132     else
3133       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3134                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3135   }
3136 }
3137
3138 #endif
3139
3140 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3141                        int key_bits)
3142 {
3143   int key[MAX_NUM_KEYS];
3144   int i;
3145
3146   /* prevent EM engine from updating time/score values parallel to GameWon() */
3147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3148       local_player->LevelSolved)
3149     return;
3150
3151   for (i = 0; i < MAX_NUM_KEYS; i++)
3152     key[i] = key_bits & (1 << i);
3153
3154   DrawGameValue_Level(level_nr);
3155
3156   DrawGameValue_Emeralds(emeralds);
3157   DrawGameValue_Dynamite(dynamite);
3158   DrawGameValue_Score(score);
3159   DrawGameValue_Time(time);
3160
3161   DrawGameValue_Keys(key);
3162 }
3163
3164 void UpdateGameDoorValues()
3165 {
3166   UpdateGameControlValues();
3167 }
3168
3169 void DrawGameDoorValues()
3170 {
3171   DisplayGameControlValues();
3172 }
3173
3174 void DrawGameDoorValues_OLD()
3175 {
3176   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3177   int dynamite_value = 0;
3178   int score_value = (local_player->LevelSolved ? local_player->score_final :
3179                      local_player->score);
3180   int gems_value = local_player->gems_still_needed;
3181   int key_bits = 0;
3182   int i, j;
3183
3184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3185   {
3186     DrawGameDoorValues_EM();
3187
3188     return;
3189   }
3190
3191   if (game.centered_player_nr == -1)
3192   {
3193     for (i = 0; i < MAX_PLAYERS; i++)
3194     {
3195       for (j = 0; j < MAX_NUM_KEYS; j++)
3196         if (stored_player[i].key[j])
3197           key_bits |= (1 << j);
3198
3199       dynamite_value += stored_player[i].inventory_size;
3200     }
3201   }
3202   else
3203   {
3204     int player_nr = game.centered_player_nr;
3205
3206     for (i = 0; i < MAX_NUM_KEYS; i++)
3207       if (stored_player[player_nr].key[i])
3208         key_bits |= (1 << i);
3209
3210     dynamite_value = stored_player[player_nr].inventory_size;
3211   }
3212
3213   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3214                     key_bits);
3215 }
3216
3217
3218 /*
3219   =============================================================================
3220   InitGameEngine()
3221   -----------------------------------------------------------------------------
3222   initialize game engine due to level / tape version number
3223   =============================================================================
3224 */
3225
3226 static void InitGameEngine()
3227 {
3228   int i, j, k, l, x, y;
3229
3230   /* set game engine from tape file when re-playing, else from level file */
3231   game.engine_version = (tape.playing ? tape.engine_version :
3232                          level.game_version);
3233
3234   /* ---------------------------------------------------------------------- */
3235   /* set flags for bugs and changes according to active game engine version */
3236   /* ---------------------------------------------------------------------- */
3237
3238   /*
3239     Summary of bugfix/change:
3240     Fixed handling for custom elements that change when pushed by the player.
3241
3242     Fixed/changed in version:
3243     3.1.0
3244
3245     Description:
3246     Before 3.1.0, custom elements that "change when pushing" changed directly
3247     after the player started pushing them (until then handled in "DigField()").
3248     Since 3.1.0, these custom elements are not changed until the "pushing"
3249     move of the element is finished (now handled in "ContinueMoving()").
3250
3251     Affected levels/tapes:
3252     The first condition is generally needed for all levels/tapes before version
3253     3.1.0, which might use the old behaviour before it was changed; known tapes
3254     that are affected are some tapes from the level set "Walpurgis Gardens" by
3255     Jamie Cullen.
3256     The second condition is an exception from the above case and is needed for
3257     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3258     above (including some development versions of 3.1.0), but before it was
3259     known that this change would break tapes like the above and was fixed in
3260     3.1.1, so that the changed behaviour was active although the engine version
3261     while recording maybe was before 3.1.0. There is at least one tape that is
3262     affected by this exception, which is the tape for the one-level set "Bug
3263     Machine" by Juergen Bonhagen.
3264   */
3265
3266   game.use_change_when_pushing_bug =
3267     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3268      !(tape.playing &&
3269        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3270        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3271
3272   /*
3273     Summary of bugfix/change:
3274     Fixed handling for blocking the field the player leaves when moving.
3275
3276     Fixed/changed in version:
3277     3.1.1
3278
3279     Description:
3280     Before 3.1.1, when "block last field when moving" was enabled, the field
3281     the player is leaving when moving was blocked for the time of the move,
3282     and was directly unblocked afterwards. This resulted in the last field
3283     being blocked for exactly one less than the number of frames of one player
3284     move. Additionally, even when blocking was disabled, the last field was
3285     blocked for exactly one frame.
3286     Since 3.1.1, due to changes in player movement handling, the last field
3287     is not blocked at all when blocking is disabled. When blocking is enabled,
3288     the last field is blocked for exactly the number of frames of one player
3289     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3290     last field is blocked for exactly one more than the number of frames of
3291     one player move.
3292
3293     Affected levels/tapes:
3294     (!!! yet to be determined -- probably many !!!)
3295   */
3296
3297   game.use_block_last_field_bug =
3298     (game.engine_version < VERSION_IDENT(3,1,1,0));
3299
3300   /*
3301     Summary of bugfix/change:
3302     Changed behaviour of CE changes with multiple changes per single frame.
3303
3304     Fixed/changed in version:
3305     3.2.0-6
3306
3307     Description:
3308     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3309     This resulted in race conditions where CEs seem to behave strange in some
3310     situations (where triggered CE changes were just skipped because there was
3311     already a CE change on that tile in the playfield in that engine frame).
3312     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3313     (The number of changes per frame must be limited in any case, because else
3314     it is easily possible to define CE changes that would result in an infinite
3315     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3316     should be set large enough so that it would only be reached in cases where
3317     the corresponding CE change conditions run into a loop. Therefore, it seems
3318     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3319     maximal number of change pages for custom elements.)
3320
3321     Affected levels/tapes:
3322     Probably many.
3323   */
3324
3325 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3326   game.max_num_changes_per_frame = 1;
3327 #else
3328   game.max_num_changes_per_frame =
3329     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3330 #endif
3331
3332   /* ---------------------------------------------------------------------- */
3333
3334   /* default scan direction: scan playfield from top/left to bottom/right */
3335   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3336
3337   /* dynamically adjust element properties according to game engine version */
3338   InitElementPropertiesEngine(game.engine_version);
3339
3340 #if 0
3341   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3342   printf("          tape version == %06d [%s] [file: %06d]\n",
3343          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3344          tape.file_version);
3345   printf("       => game.engine_version == %06d\n", game.engine_version);
3346 #endif
3347
3348   /* ---------- initialize player's initial move delay --------------------- */
3349
3350   /* dynamically adjust player properties according to level information */
3351   for (i = 0; i < MAX_PLAYERS; i++)
3352     game.initial_move_delay_value[i] =
3353       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3354
3355   /* dynamically adjust player properties according to game engine version */
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357     game.initial_move_delay[i] =
3358       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3359        game.initial_move_delay_value[i] : 0);
3360
3361   /* ---------- initialize player's initial push delay --------------------- */
3362
3363   /* dynamically adjust player properties according to game engine version */
3364   game.initial_push_delay_value =
3365     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3366
3367   /* ---------- initialize changing elements ------------------------------- */
3368
3369   /* initialize changing elements information */
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371   {
3372     struct ElementInfo *ei = &element_info[i];
3373
3374     /* this pointer might have been changed in the level editor */
3375     ei->change = &ei->change_page[0];
3376
3377     if (!IS_CUSTOM_ELEMENT(i))
3378     {
3379       ei->change->target_element = EL_EMPTY_SPACE;
3380       ei->change->delay_fixed = 0;
3381       ei->change->delay_random = 0;
3382       ei->change->delay_frames = 1;
3383     }
3384
3385     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3386     {
3387       ei->has_change_event[j] = FALSE;
3388
3389       ei->event_page_nr[j] = 0;
3390       ei->event_page[j] = &ei->change_page[0];
3391     }
3392   }
3393
3394   /* add changing elements from pre-defined list */
3395   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3396   {
3397     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3398     struct ElementInfo *ei = &element_info[ch_delay->element];
3399
3400     ei->change->target_element       = ch_delay->target_element;
3401     ei->change->delay_fixed          = ch_delay->change_delay;
3402
3403     ei->change->pre_change_function  = ch_delay->pre_change_function;
3404     ei->change->change_function      = ch_delay->change_function;
3405     ei->change->post_change_function = ch_delay->post_change_function;
3406
3407     ei->change->can_change = TRUE;
3408     ei->change->can_change_or_has_action = TRUE;
3409
3410     ei->has_change_event[CE_DELAY] = TRUE;
3411
3412     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3413     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3414   }
3415
3416   /* ---------- initialize internal run-time variables --------------------- */
3417
3418   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3419   {
3420     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3421
3422     for (j = 0; j < ei->num_change_pages; j++)
3423     {
3424       ei->change_page[j].can_change_or_has_action =
3425         (ei->change_page[j].can_change |
3426          ei->change_page[j].has_action);
3427     }
3428   }
3429
3430   /* add change events from custom element configuration */
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3434
3435     for (j = 0; j < ei->num_change_pages; j++)
3436     {
3437       if (!ei->change_page[j].can_change_or_has_action)
3438         continue;
3439
3440       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3441       {
3442         /* only add event page for the first page found with this event */
3443         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3444         {
3445           ei->has_change_event[k] = TRUE;
3446
3447           ei->event_page_nr[k] = j;
3448           ei->event_page[k] = &ei->change_page[j];
3449         }
3450       }
3451     }
3452   }
3453
3454 #if 1
3455   /* ---------- initialize reference elements in change conditions --------- */
3456
3457   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3458   {
3459     int element = EL_CUSTOM_START + i;
3460     struct ElementInfo *ei = &element_info[element];
3461
3462     for (j = 0; j < ei->num_change_pages; j++)
3463     {
3464       int trigger_element = ei->change_page[j].initial_trigger_element;
3465
3466       if (trigger_element >= EL_PREV_CE_8 &&
3467           trigger_element <= EL_NEXT_CE_8)
3468         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3469
3470       ei->change_page[j].trigger_element = trigger_element;
3471     }
3472   }
3473 #endif
3474
3475   /* ---------- initialize run-time trigger player and element ------------- */
3476
3477   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3478   {
3479     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3480
3481     for (j = 0; j < ei->num_change_pages; j++)
3482     {
3483       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3484       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3485       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3486       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3487       ei->change_page[j].actual_trigger_ce_value = 0;
3488       ei->change_page[j].actual_trigger_ce_score = 0;
3489     }
3490   }
3491
3492   /* ---------- initialize trigger events ---------------------------------- */
3493
3494   /* initialize trigger events information */
3495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3497       trigger_events[i][j] = FALSE;
3498
3499   /* add trigger events from element change event properties */
3500   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3501   {
3502     struct ElementInfo *ei = &element_info[i];
3503
3504     for (j = 0; j < ei->num_change_pages; j++)
3505     {
3506       if (!ei->change_page[j].can_change_or_has_action)
3507         continue;
3508
3509       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3510       {
3511         int trigger_element = ei->change_page[j].trigger_element;
3512
3513         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3514         {
3515           if (ei->change_page[j].has_event[k])
3516           {
3517             if (IS_GROUP_ELEMENT(trigger_element))
3518             {
3519               struct ElementGroupInfo *group =
3520                 element_info[trigger_element].group;
3521
3522               for (l = 0; l < group->num_elements_resolved; l++)
3523                 trigger_events[group->element_resolved[l]][k] = TRUE;
3524             }
3525             else if (trigger_element == EL_ANY_ELEMENT)
3526               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3527                 trigger_events[l][k] = TRUE;
3528             else
3529               trigger_events[trigger_element][k] = TRUE;
3530           }
3531         }
3532       }
3533     }
3534   }
3535
3536   /* ---------- initialize push delay -------------------------------------- */
3537
3538   /* initialize push delay values to default */
3539   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540   {
3541     if (!IS_CUSTOM_ELEMENT(i))
3542     {
3543       /* set default push delay values (corrected since version 3.0.7-1) */
3544       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3545       {
3546         element_info[i].push_delay_fixed = 2;
3547         element_info[i].push_delay_random = 8;
3548       }
3549       else
3550       {
3551         element_info[i].push_delay_fixed = 8;
3552         element_info[i].push_delay_random = 8;
3553       }
3554     }
3555   }
3556
3557   /* set push delay value for certain elements from pre-defined list */
3558   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3559   {
3560     int e = push_delay_list[i].element;
3561
3562     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3563     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3564   }
3565
3566   /* set push delay value for Supaplex elements for newer engine versions */
3567   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3568   {
3569     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570     {
3571       if (IS_SP_ELEMENT(i))
3572       {
3573         /* set SP push delay to just enough to push under a falling zonk */
3574         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3575
3576         element_info[i].push_delay_fixed  = delay;
3577         element_info[i].push_delay_random = 0;
3578       }
3579     }
3580   }
3581
3582   /* ---------- initialize move stepsize ----------------------------------- */
3583
3584   /* initialize move stepsize values to default */
3585   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586     if (!IS_CUSTOM_ELEMENT(i))
3587       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3588
3589   /* set move stepsize value for certain elements from pre-defined list */
3590   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3591   {
3592     int e = move_stepsize_list[i].element;
3593
3594     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3595   }
3596
3597   /* ---------- initialize collect score ----------------------------------- */
3598
3599   /* initialize collect score values for custom elements from initial value */
3600   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601     if (IS_CUSTOM_ELEMENT(i))
3602       element_info[i].collect_score = element_info[i].collect_score_initial;
3603
3604   /* ---------- initialize collect count ----------------------------------- */
3605
3606   /* initialize collect count values for non-custom elements */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (!IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_count_initial = 0;
3610
3611   /* add collect count values for all elements from pre-defined list */
3612   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3613     element_info[collect_count_list[i].element].collect_count_initial =
3614       collect_count_list[i].count;
3615
3616   /* ---------- initialize access direction -------------------------------- */
3617
3618   /* initialize access direction values to default (access from every side) */
3619   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3620     if (!IS_CUSTOM_ELEMENT(i))
3621       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3622
3623   /* set access direction value for certain elements from pre-defined list */
3624   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3625     element_info[access_direction_list[i].element].access_direction =
3626       access_direction_list[i].direction;
3627
3628   /* ---------- initialize explosion content ------------------------------- */
3629   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3630   {
3631     if (IS_CUSTOM_ELEMENT(i))
3632       continue;
3633
3634     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3635     {
3636       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3637
3638       element_info[i].content.e[x][y] =
3639         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3640          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3641          i == EL_PLAYER_3 ? EL_EMERALD :
3642          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3643          i == EL_MOLE ? EL_EMERALD_RED :
3644          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3645          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3646          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3647          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3648          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3649          i == EL_WALL_EMERALD ? EL_EMERALD :
3650          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3651          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3652          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3653          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3654          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3655          i == EL_WALL_PEARL ? EL_PEARL :
3656          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3657          EL_EMPTY);
3658     }
3659   }
3660
3661   /* ---------- initialize recursion detection ------------------------------ */
3662   recursion_loop_depth = 0;
3663   recursion_loop_detected = FALSE;
3664   recursion_loop_element = EL_UNDEFINED;
3665
3666   /* ---------- initialize graphics engine ---------------------------------- */
3667   game.scroll_delay_value =
3668     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3669      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3670   game.scroll_delay_value =
3671     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3672 }
3673
3674 int get_num_special_action(int element, int action_first, int action_last)
3675 {
3676   int num_special_action = 0;
3677   int i, j;
3678
3679   for (i = action_first; i <= action_last; i++)
3680   {
3681     boolean found = FALSE;
3682
3683     for (j = 0; j < NUM_DIRECTIONS; j++)
3684       if (el_act_dir2img(element, i, j) !=
3685           el_act_dir2img(element, ACTION_DEFAULT, j))
3686         found = TRUE;
3687
3688     if (found)
3689       num_special_action++;
3690     else
3691       break;
3692   }
3693
3694   return num_special_action;
3695 }
3696
3697
3698 /*
3699   =============================================================================
3700   InitGame()
3701   -----------------------------------------------------------------------------
3702   initialize and start new game
3703   =============================================================================
3704 */
3705
3706 void InitGame()
3707 {
3708   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3709   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3710   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3711 #if 0
3712   boolean do_fading = (game_status == GAME_MODE_MAIN);
3713 #endif
3714 #if 1
3715   int initial_move_dir = MV_DOWN;
3716 #else
3717   int initial_move_dir = MV_NONE;
3718 #endif
3719   int i, j, x, y;
3720
3721   game_status = GAME_MODE_PLAYING;
3722
3723 #if 1
3724   /* needed if different viewport properties defined for playing */
3725   ChangeViewportPropertiesIfNeeded();
3726 #endif
3727
3728   InitGameEngine();
3729   InitGameControlValues();
3730
3731   /* don't play tapes over network */
3732   network_playing = (options.network && !tape.playing);
3733
3734   for (i = 0; i < MAX_PLAYERS; i++)
3735   {
3736     struct PlayerInfo *player = &stored_player[i];
3737
3738     player->index_nr = i;
3739     player->index_bit = (1 << i);
3740     player->element_nr = EL_PLAYER_1 + i;
3741
3742     player->present = FALSE;
3743     player->active = FALSE;
3744     player->mapped = FALSE;
3745
3746     player->killed = FALSE;
3747     player->reanimated = FALSE;
3748
3749     player->action = 0;
3750     player->effective_action = 0;
3751     player->programmed_action = 0;
3752
3753     player->score = 0;
3754     player->score_final = 0;
3755
3756     player->gems_still_needed = level.gems_needed;
3757     player->sokobanfields_still_needed = 0;
3758     player->lights_still_needed = 0;
3759     player->friends_still_needed = 0;
3760
3761     for (j = 0; j < MAX_NUM_KEYS; j++)
3762       player->key[j] = FALSE;
3763
3764     player->num_white_keys = 0;
3765
3766     player->dynabomb_count = 0;
3767     player->dynabomb_size = 1;
3768     player->dynabombs_left = 0;
3769     player->dynabomb_xl = FALSE;
3770
3771     player->MovDir = initial_move_dir;
3772     player->MovPos = 0;
3773     player->GfxPos = 0;
3774     player->GfxDir = initial_move_dir;
3775     player->GfxAction = ACTION_DEFAULT;
3776     player->Frame = 0;
3777     player->StepFrame = 0;
3778
3779     player->initial_element = player->element_nr;
3780     player->artwork_element =
3781       (level.use_artwork_element[i] ? level.artwork_element[i] :
3782        player->element_nr);
3783     player->use_murphy = FALSE;
3784
3785     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3786     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3787
3788     player->gravity = level.initial_player_gravity[i];
3789
3790     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3791
3792     player->actual_frame_counter = 0;
3793
3794     player->step_counter = 0;
3795
3796     player->last_move_dir = initial_move_dir;
3797
3798     player->is_active = FALSE;
3799
3800     player->is_waiting = FALSE;
3801     player->is_moving = FALSE;
3802     player->is_auto_moving = FALSE;
3803     player->is_digging = FALSE;
3804     player->is_snapping = FALSE;
3805     player->is_collecting = FALSE;
3806     player->is_pushing = FALSE;
3807     player->is_switching = FALSE;
3808     player->is_dropping = FALSE;
3809     player->is_dropping_pressed = FALSE;
3810
3811     player->is_bored = FALSE;
3812     player->is_sleeping = FALSE;
3813
3814     player->frame_counter_bored = -1;
3815     player->frame_counter_sleeping = -1;
3816
3817     player->anim_delay_counter = 0;
3818     player->post_delay_counter = 0;
3819
3820     player->dir_waiting = initial_move_dir;
3821     player->action_waiting = ACTION_DEFAULT;
3822     player->last_action_waiting = ACTION_DEFAULT;
3823     player->special_action_bored = ACTION_DEFAULT;
3824     player->special_action_sleeping = ACTION_DEFAULT;
3825
3826     player->switch_x = -1;
3827     player->switch_y = -1;
3828
3829     player->drop_x = -1;
3830     player->drop_y = -1;
3831
3832     player->show_envelope = 0;
3833
3834     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3835
3836     player->push_delay       = -1;      /* initialized when pushing starts */
3837     player->push_delay_value = game.initial_push_delay_value;
3838
3839     player->drop_delay = 0;
3840     player->drop_pressed_delay = 0;
3841
3842     player->last_jx = -1;
3843     player->last_jy = -1;
3844     player->jx = -1;
3845     player->jy = -1;
3846
3847     player->shield_normal_time_left = 0;
3848     player->shield_deadly_time_left = 0;
3849
3850     player->inventory_infinite_element = EL_UNDEFINED;
3851     player->inventory_size = 0;
3852
3853     if (level.use_initial_inventory[i])
3854     {
3855       for (j = 0; j < level.initial_inventory_size[i]; j++)
3856       {
3857         int element = level.initial_inventory_content[i][j];
3858         int collect_count = element_info[element].collect_count_initial;
3859         int k;
3860
3861         if (!IS_CUSTOM_ELEMENT(element))
3862           collect_count = 1;
3863
3864         if (collect_count == 0)
3865           player->inventory_infinite_element = element;
3866         else
3867           for (k = 0; k < collect_count; k++)
3868             if (player->inventory_size < MAX_INVENTORY_SIZE)
3869               player->inventory_element[player->inventory_size++] = element;
3870       }
3871     }
3872
3873     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3874     SnapField(player, 0, 0);
3875
3876     player->LevelSolved = FALSE;
3877     player->GameOver = FALSE;
3878
3879     player->LevelSolved_GameWon = FALSE;
3880     player->LevelSolved_GameEnd = FALSE;
3881     player->LevelSolved_PanelOff = FALSE;
3882     player->LevelSolved_SaveTape = FALSE;
3883     player->LevelSolved_SaveScore = FALSE;
3884     player->LevelSolved_CountingTime = 0;
3885     player->LevelSolved_CountingScore = 0;
3886
3887     map_player_action[i] = i;
3888   }
3889
3890   network_player_action_received = FALSE;
3891
3892 #if defined(NETWORK_AVALIABLE)
3893   /* initial null action */
3894   if (network_playing)
3895     SendToServer_MovePlayer(MV_NONE);
3896 #endif
3897
3898   ZX = ZY = -1;
3899   ExitX = ExitY = -1;
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905   TapeTime = 0;
3906
3907   ScreenMovDir = MV_NONE;
3908   ScreenMovPos = 0;
3909   ScreenGfxPos = 0;
3910
3911   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3912
3913   AllPlayersGone = FALSE;
3914
3915   game.yamyam_content_nr = 0;
3916   game.robot_wheel_active = FALSE;
3917   game.magic_wall_active = FALSE;
3918   game.magic_wall_time_left = 0;
3919   game.light_time_left = 0;
3920   game.timegate_time_left = 0;
3921   game.switchgate_pos = 0;
3922   game.wind_direction = level.wind_direction_initial;
3923
3924 #if !USE_PLAYER_GRAVITY
3925   game.gravity = FALSE;
3926   game.explosions_delayed = TRUE;
3927 #endif
3928
3929   game.lenses_time_left = 0;
3930   game.magnify_time_left = 0;
3931
3932   game.ball_state = level.ball_state_initial;
3933   game.ball_content_nr = 0;
3934
3935   game.envelope_active = FALSE;
3936
3937   /* set focus to local player for network games, else to all players */
3938   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3939   game.centered_player_nr_next = game.centered_player_nr;
3940   game.set_centered_player = FALSE;
3941
3942   if (network_playing && tape.recording)
3943   {
3944     /* store client dependent player focus when recording network games */
3945     tape.centered_player_nr_next = game.centered_player_nr_next;
3946     tape.set_centered_player = TRUE;
3947   }
3948
3949   for (i = 0; i < NUM_BELTS; i++)
3950   {
3951     game.belt_dir[i] = MV_NONE;
3952     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3953   }
3954
3955   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3956     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3957
3958   SCAN_PLAYFIELD(x, y)
3959   {
3960     Feld[x][y] = level.field[x][y];
3961     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3962     ChangeDelay[x][y] = 0;
3963     ChangePage[x][y] = -1;
3964 #if USE_NEW_CUSTOM_VALUE
3965     CustomValue[x][y] = 0;              /* initialized in InitField() */
3966 #endif
3967     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3968     AmoebaNr[x][y] = 0;
3969     WasJustMoving[x][y] = 0;
3970     WasJustFalling[x][y] = 0;
3971     CheckCollision[x][y] = 0;
3972     CheckImpact[x][y] = 0;
3973     Stop[x][y] = FALSE;
3974     Pushed[x][y] = FALSE;
3975
3976     ChangeCount[x][y] = 0;
3977     ChangeEvent[x][y] = -1;
3978
3979     ExplodePhase[x][y] = 0;
3980     ExplodeDelay[x][y] = 0;
3981     ExplodeField[x][y] = EX_TYPE_NONE;
3982
3983     RunnerVisit[x][y] = 0;
3984     PlayerVisit[x][y] = 0;
3985
3986     GfxFrame[x][y] = 0;
3987     GfxRandom[x][y] = INIT_GFX_RANDOM();
3988     GfxElement[x][y] = EL_UNDEFINED;
3989     GfxAction[x][y] = ACTION_DEFAULT;
3990     GfxDir[x][y] = MV_NONE;
3991     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3992   }
3993
3994   SCAN_PLAYFIELD(x, y)
3995   {
3996     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3997       emulate_bd = FALSE;
3998     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3999       emulate_sb = FALSE;
4000     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4001       emulate_sp = FALSE;
4002
4003     InitField(x, y, TRUE);
4004
4005     ResetGfxAnimation(x, y);
4006   }
4007
4008   InitBeltMovement();
4009
4010   for (i = 0; i < MAX_PLAYERS; i++)
4011   {
4012     struct PlayerInfo *player = &stored_player[i];
4013
4014     /* set number of special actions for bored and sleeping animation */
4015     player->num_special_action_bored =
4016       get_num_special_action(player->artwork_element,
4017                              ACTION_BORING_1, ACTION_BORING_LAST);
4018     player->num_special_action_sleeping =
4019       get_num_special_action(player->artwork_element,
4020                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4021   }
4022
4023   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4024                     emulate_sb ? EMU_SOKOBAN :
4025                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4026
4027 #if USE_NEW_ALL_SLIPPERY
4028   /* initialize type of slippery elements */
4029   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4030   {
4031     if (!IS_CUSTOM_ELEMENT(i))
4032     {
4033       /* default: elements slip down either to the left or right randomly */
4034       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4035
4036       /* SP style elements prefer to slip down on the left side */
4037       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4038         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4039
4040       /* BD style elements prefer to slip down on the left side */
4041       if (game.emulation == EMU_BOULDERDASH)
4042         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4043     }
4044   }
4045 #endif
4046
4047   /* initialize explosion and ignition delay */
4048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4049   {
4050     if (!IS_CUSTOM_ELEMENT(i))
4051     {
4052       int num_phase = 8;
4053       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4054                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4055                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4056       int last_phase = (num_phase + 1) * delay;
4057       int half_phase = (num_phase / 2) * delay;
4058
4059       element_info[i].explosion_delay = last_phase - 1;
4060       element_info[i].ignition_delay = half_phase;
4061
4062       if (i == EL_BLACK_ORB)
4063         element_info[i].ignition_delay = 1;
4064     }
4065
4066 #if 0
4067     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4068       element_info[i].explosion_delay = 1;
4069
4070     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4071       element_info[i].ignition_delay = 1;
4072 #endif
4073   }
4074
4075   /* correct non-moving belts to start moving left */
4076   for (i = 0; i < NUM_BELTS; i++)
4077     if (game.belt_dir[i] == MV_NONE)
4078       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4079
4080 #if USE_NEW_PLAYER_ASSIGNMENTS
4081   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4082   /* choose default local player */
4083   local_player = &stored_player[0];
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     stored_player[i].connected = FALSE;
4087
4088   local_player->connected = TRUE;
4089   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4090
4091   if (tape.playing)
4092   {
4093     /* try to guess locally connected team mode players (needed for correct
4094        assignment of player figures from level to locally playing players) */
4095
4096     for (i = 0; i < MAX_PLAYERS; i++)
4097       if (tape.player_participates[i])
4098         stored_player[i].connected = TRUE;
4099   }
4100   else if (setup.team_mode && !options.network)
4101   {
4102     /* try to guess locally connected team mode players (needed for correct
4103        assignment of player figures from level to locally playing players) */
4104
4105     for (i = 0; i < MAX_PLAYERS; i++)
4106       if (setup.input[i].use_joystick ||
4107           setup.input[i].key.left != KSYM_UNDEFINED)
4108         stored_player[i].connected = TRUE;
4109   }
4110
4111 #if 0
4112   for (i = 0; i < MAX_PLAYERS; i++)
4113     printf("::: player %d: %s\n", i,
4114            (stored_player[i].connected ? "connected" : "not connected"));
4115
4116   for (i = 0; i < MAX_PLAYERS; i++)
4117     printf("::: player %d: %s\n", i,
4118            (stored_player[i].present ? "present" : "not present"));
4119 #endif
4120
4121   /* check if any connected player was not found in playfield */
4122   for (i = 0; i < MAX_PLAYERS; i++)
4123   {
4124     struct PlayerInfo *player = &stored_player[i];
4125
4126     if (player->connected && !player->present)
4127     {
4128       struct PlayerInfo *field_player = NULL;
4129
4130 #if 0
4131       printf("::: looking for field player for player %d ...\n", i);
4132 #endif
4133
4134       /* assign first free player found that is present in the playfield */
4135
4136       /* first try: look for unmapped playfield player that is not connected */
4137       if (field_player == NULL)
4138         for (j = 0; j < MAX_PLAYERS; j++)
4139           if (stored_player[j].present &&
4140               !stored_player[j].mapped &&
4141               !stored_player[j].connected)
4142             field_player = &stored_player[j];
4143
4144       /* second try: look for *any* unmapped playfield player */
4145       if (field_player == NULL)
4146         for (j = 0; j < MAX_PLAYERS; j++)
4147           if (stored_player[j].present &&
4148               !stored_player[j].mapped)
4149             field_player = &stored_player[j];
4150
4151       if (field_player != NULL)
4152       {
4153         int jx = field_player->jx, jy = field_player->jy;
4154
4155 #if 0
4156         printf("::: found player figure %d\n", field_player->index_nr);
4157 #endif
4158
4159         player->present = FALSE;
4160         player->active = FALSE;
4161
4162         field_player->present = TRUE;
4163         field_player->active = TRUE;
4164
4165         /*
4166         player->initial_element = field_player->initial_element;
4167         player->artwork_element = field_player->artwork_element;
4168
4169         player->block_last_field       = field_player->block_last_field;
4170         player->block_delay_adjustment = field_player->block_delay_adjustment;
4171         */
4172
4173         StorePlayer[jx][jy] = field_player->element_nr;
4174
4175         field_player->jx = field_player->last_jx = jx;
4176         field_player->jy = field_player->last_jy = jy;
4177
4178         if (local_player == player)
4179           local_player = field_player;
4180
4181         map_player_action[field_player->index_nr] = i;
4182
4183         field_player->mapped = TRUE;
4184
4185 #if 0
4186         printf("::: map_player_action[%d] == %d\n",
4187                field_player->index_nr, i);
4188 #endif
4189       }
4190     }
4191
4192     if (player->connected && player->present)
4193       player->mapped = TRUE;
4194   }
4195
4196 #else
4197
4198   /* check if any connected player was not found in playfield */
4199   for (i = 0; i < MAX_PLAYERS; i++)
4200   {
4201     struct PlayerInfo *player = &stored_player[i];
4202
4203     if (player->connected && !player->present)
4204     {
4205       for (j = 0; j < MAX_PLAYERS; j++)
4206       {
4207         struct PlayerInfo *field_player = &stored_player[j];
4208         int jx = field_player->jx, jy = field_player->jy;
4209
4210         /* assign first free player found that is present in the playfield */
4211         if (field_player->present && !field_player->connected)
4212         {
4213           player->present = TRUE;
4214           player->active = TRUE;
4215
4216           field_player->present = FALSE;
4217           field_player->active = FALSE;
4218
4219           player->initial_element = field_player->initial_element;
4220           player->artwork_element = field_player->artwork_element;
4221
4222           player->block_last_field       = field_player->block_last_field;
4223           player->block_delay_adjustment = field_player->block_delay_adjustment;
4224
4225           StorePlayer[jx][jy] = player->element_nr;
4226
4227           player->jx = player->last_jx = jx;
4228           player->jy = player->last_jy = jy;
4229
4230           break;
4231         }
4232       }
4233     }
4234   }
4235 #endif
4236
4237 #if 0
4238   printf("::: local_player->present == %d\n", local_player->present);
4239 #endif
4240
4241   if (tape.playing)
4242   {
4243     /* when playing a tape, eliminate all players who do not participate */
4244
4245 #if USE_NEW_PLAYER_ASSIGNMENTS
4246     for (i = 0; i < MAX_PLAYERS; i++)
4247     {
4248       if (stored_player[i].active &&
4249           !tape.player_participates[map_player_action[i]])
4250       {
4251         struct PlayerInfo *player = &stored_player[i];
4252         int jx = player->jx, jy = player->jy;
4253
4254         player->active = FALSE;
4255         StorePlayer[jx][jy] = 0;
4256         Feld[jx][jy] = EL_EMPTY;
4257       }
4258     }
4259 #else
4260     for (i = 0; i < MAX_PLAYERS; i++)
4261     {
4262       if (stored_player[i].active &&
4263           !tape.player_participates[i])
4264       {
4265         struct PlayerInfo *player = &stored_player[i];
4266         int jx = player->jx, jy = player->jy;
4267
4268         player->active = FALSE;
4269         StorePlayer[jx][jy] = 0;
4270         Feld[jx][jy] = EL_EMPTY;
4271       }
4272     }
4273 #endif
4274   }
4275   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4276   {
4277     /* when in single player mode, eliminate all but the first active player */
4278
4279     for (i = 0; i < MAX_PLAYERS; i++)
4280     {
4281       if (stored_player[i].active)
4282       {
4283         for (j = i + 1; j < MAX_PLAYERS; j++)
4284         {
4285           if (stored_player[j].active)
4286           {
4287             struct PlayerInfo *player = &stored_player[j];
4288             int jx = player->jx, jy = player->jy;
4289
4290             player->active = FALSE;
4291             player->present = FALSE;
4292
4293             StorePlayer[jx][jy] = 0;
4294             Feld[jx][jy] = EL_EMPTY;
4295           }
4296         }
4297       }
4298     }
4299   }
4300
4301   /* when recording the game, store which players take part in the game */
4302   if (tape.recording)
4303   {
4304 #if USE_NEW_PLAYER_ASSIGNMENTS
4305     for (i = 0; i < MAX_PLAYERS; i++)
4306       if (stored_player[i].connected)
4307         tape.player_participates[i] = TRUE;
4308 #else
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].active)
4311         tape.player_participates[i] = TRUE;
4312 #endif
4313   }
4314
4315   if (options.debug)
4316   {
4317     for (i = 0; i < MAX_PLAYERS; i++)
4318     {
4319       struct PlayerInfo *player = &stored_player[i];
4320
4321       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4322              i+1,
4323              player->present,
4324              player->connected,
4325              player->active);
4326       if (local_player == player)
4327         printf("Player  %d is local player.\n", i+1);
4328     }
4329   }
4330
4331   if (BorderElement == EL_EMPTY)
4332   {
4333     SBX_Left = 0;
4334     SBX_Right = lev_fieldx - SCR_FIELDX;
4335     SBY_Upper = 0;
4336     SBY_Lower = lev_fieldy - SCR_FIELDY;
4337   }
4338   else
4339   {
4340     SBX_Left = -1;
4341     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4342     SBY_Upper = -1;
4343     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4344   }
4345
4346   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4347     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4348
4349   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4350     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4351
4352   /* if local player not found, look for custom element that might create
4353      the player (make some assumptions about the right custom element) */
4354   if (!local_player->present)
4355   {
4356     int start_x = 0, start_y = 0;
4357     int found_rating = 0;
4358     int found_element = EL_UNDEFINED;
4359     int player_nr = local_player->index_nr;
4360
4361     SCAN_PLAYFIELD(x, y)
4362     {
4363       int element = Feld[x][y];
4364       int content;
4365       int xx, yy;
4366       boolean is_player;
4367
4368       if (level.use_start_element[player_nr] &&
4369           level.start_element[player_nr] == element &&
4370           found_rating < 4)
4371       {
4372         start_x = x;
4373         start_y = y;
4374
4375         found_rating = 4;
4376         found_element = element;
4377       }
4378
4379       if (!IS_CUSTOM_ELEMENT(element))
4380         continue;
4381
4382       if (CAN_CHANGE(element))
4383       {
4384         for (i = 0; i < element_info[element].num_change_pages; i++)
4385         {
4386           /* check for player created from custom element as single target */
4387           content = element_info[element].change_page[i].target_element;
4388           is_player = ELEM_IS_PLAYER(content);
4389
4390           if (is_player && (found_rating < 3 ||
4391                             (found_rating == 3 && element < found_element)))
4392           {
4393             start_x = x;
4394             start_y = y;
4395
4396             found_rating = 3;
4397             found_element = element;
4398           }
4399         }
4400       }
4401
4402       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4403       {
4404         /* check for player created from custom element as explosion content */
4405         content = element_info[element].content.e[xx][yy];
4406         is_player = ELEM_IS_PLAYER(content);
4407
4408         if (is_player && (found_rating < 2 ||
4409                           (found_rating == 2 && element < found_element)))
4410         {
4411           start_x = x + xx - 1;
4412           start_y = y + yy - 1;
4413
4414           found_rating = 2;
4415           found_element = element;
4416         }
4417
4418         if (!CAN_CHANGE(element))
4419           continue;
4420
4421         for (i = 0; i < element_info[element].num_change_pages; i++)
4422         {
4423           /* check for player created from custom element as extended target */
4424           content =
4425             element_info[element].change_page[i].target_content.e[xx][yy];
4426
4427           is_player = ELEM_IS_PLAYER(content);
4428
4429           if (is_player && (found_rating < 1 ||
4430                             (found_rating == 1 && element < found_element)))
4431           {
4432             start_x = x + xx - 1;
4433             start_y = y + yy - 1;
4434
4435             found_rating = 1;
4436             found_element = element;
4437           }
4438         }
4439       }
4440     }
4441
4442     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4443                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4444                 start_x - MIDPOSX);
4445
4446     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4447                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4448                 start_y - MIDPOSY);
4449   }
4450   else
4451   {
4452     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4453                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4454                 local_player->jx - MIDPOSX);
4455
4456     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4457                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4458                 local_player->jy - MIDPOSY);
4459   }
4460
4461 #if 0
4462   /* do not use PLAYING mask for fading out from main screen */
4463   game_status = GAME_MODE_MAIN;
4464 #endif
4465
4466   StopAnimation();
4467
4468   if (!game.restart_level)
4469     CloseDoor(DOOR_CLOSE_1);
4470
4471 #if 1
4472   if (level_editor_test_game)
4473     FadeSkipNextFadeIn();
4474   else
4475     FadeSetEnterScreen();
4476 #else
4477   if (level_editor_test_game)
4478     fading = fading_none;
4479   else
4480     fading = menu.destination;
4481 #endif
4482
4483 #if 1
4484   FadeOut(REDRAW_FIELD);
4485 #else
4486   if (do_fading)
4487     FadeOut(REDRAW_FIELD);
4488 #endif
4489
4490 #if 0
4491   game_status = GAME_MODE_PLAYING;
4492 #endif
4493
4494   /* !!! FIX THIS (START) !!! */
4495   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4496   {
4497     InitGameEngine_EM();
4498
4499     /* blit playfield from scroll buffer to normal back buffer for fading in */
4500     BlitScreenToBitmap_EM(backbuffer);
4501   }
4502   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4503   {
4504     InitGameEngine_SP();
4505
4506     /* blit playfield from scroll buffer to normal back buffer for fading in */
4507     BlitScreenToBitmap_SP(backbuffer);
4508   }
4509   else
4510   {
4511     DrawLevel();
4512     DrawAllPlayers();
4513
4514     /* after drawing the level, correct some elements */
4515     if (game.timegate_time_left == 0)
4516       CloseAllOpenTimegates();
4517
4518     /* blit playfield from scroll buffer to normal back buffer for fading in */
4519     if (setup.soft_scrolling)
4520       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4521
4522     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4523   }
4524   /* !!! FIX THIS (END) !!! */
4525
4526 #if 1
4527   FadeIn(REDRAW_FIELD);
4528 #else
4529   if (do_fading)
4530     FadeIn(REDRAW_FIELD);
4531
4532   BackToFront();
4533 #endif
4534
4535   if (!game.restart_level)
4536   {
4537     /* copy default game door content to main double buffer */
4538     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4539                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4540   }
4541
4542   SetPanelBackground();
4543   SetDrawBackgroundMask(REDRAW_DOOR_1);
4544
4545 #if 1
4546   UpdateAndDisplayGameControlValues();
4547 #else
4548   UpdateGameDoorValues();
4549   DrawGameDoorValues();
4550 #endif
4551
4552   if (!game.restart_level)
4553   {
4554     UnmapGameButtons();
4555     UnmapTapeButtons();
4556     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4557     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4558     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4559     MapGameButtons();
4560     MapTapeButtons();
4561
4562     /* copy actual game door content to door double buffer for OpenDoor() */
4563     BlitBitmap(drawto, bitmap_db_door,
4564                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4565
4566     OpenDoor(DOOR_OPEN_ALL);
4567
4568     PlaySound(SND_GAME_STARTING);
4569
4570     if (setup.sound_music)
4571       PlayLevelMusic();
4572
4573     KeyboardAutoRepeatOffUnlessAutoplay();
4574
4575     if (options.debug)
4576     {
4577       for (i = 0; i < MAX_PLAYERS; i++)
4578         printf("Player %d %sactive.\n",
4579                i + 1, (stored_player[i].active ? "" : "not "));
4580     }
4581   }
4582
4583 #if 1
4584   UnmapAllGadgets();
4585
4586   MapGameButtons();
4587   MapTapeButtons();
4588 #endif
4589
4590   game.restart_level = FALSE;
4591 }
4592
4593 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4594 {
4595   /* this is used for non-R'n'D game engines to update certain engine values */
4596
4597   /* needed to determine if sounds are played within the visible screen area */
4598   scroll_x = actual_scroll_x;
4599   scroll_y = actual_scroll_y;
4600 }
4601
4602 void InitMovDir(int x, int y)
4603 {
4604   int i, element = Feld[x][y];
4605   static int xy[4][2] =
4606   {
4607     {  0, +1 },
4608     { +1,  0 },
4609     {  0, -1 },
4610     { -1,  0 }
4611   };
4612   static int direction[3][4] =
4613   {
4614     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4615     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4616     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4617   };
4618
4619   switch (element)
4620   {
4621     case EL_BUG_RIGHT:
4622     case EL_BUG_UP:
4623     case EL_BUG_LEFT:
4624     case EL_BUG_DOWN:
4625       Feld[x][y] = EL_BUG;
4626       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4627       break;
4628
4629     case EL_SPACESHIP_RIGHT:
4630     case EL_SPACESHIP_UP:
4631     case EL_SPACESHIP_LEFT:
4632     case EL_SPACESHIP_DOWN:
4633       Feld[x][y] = EL_SPACESHIP;
4634       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4635       break;
4636
4637     case EL_BD_BUTTERFLY_RIGHT:
4638     case EL_BD_BUTTERFLY_UP:
4639     case EL_BD_BUTTERFLY_LEFT:
4640     case EL_BD_BUTTERFLY_DOWN:
4641       Feld[x][y] = EL_BD_BUTTERFLY;
4642       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4643       break;
4644
4645     case EL_BD_FIREFLY_RIGHT:
4646     case EL_BD_FIREFLY_UP:
4647     case EL_BD_FIREFLY_LEFT:
4648     case EL_BD_FIREFLY_DOWN:
4649       Feld[x][y] = EL_BD_FIREFLY;
4650       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4651       break;
4652
4653     case EL_PACMAN_RIGHT:
4654     case EL_PACMAN_UP:
4655     case EL_PACMAN_LEFT:
4656     case EL_PACMAN_DOWN:
4657       Feld[x][y] = EL_PACMAN;
4658       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4659       break;
4660
4661     case EL_YAMYAM_LEFT:
4662     case EL_YAMYAM_RIGHT:
4663     case EL_YAMYAM_UP:
4664     case EL_YAMYAM_DOWN:
4665       Feld[x][y] = EL_YAMYAM;
4666       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4667       break;
4668
4669     case EL_SP_SNIKSNAK:
4670       MovDir[x][y] = MV_UP;
4671       break;
4672
4673     case EL_SP_ELECTRON:
4674       MovDir[x][y] = MV_LEFT;
4675       break;
4676
4677     case EL_MOLE_LEFT:
4678     case EL_MOLE_RIGHT:
4679     case EL_MOLE_UP:
4680     case EL_MOLE_DOWN:
4681       Feld[x][y] = EL_MOLE;
4682       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4683       break;
4684
4685     default:
4686       if (IS_CUSTOM_ELEMENT(element))
4687       {
4688         struct ElementInfo *ei = &element_info[element];
4689         int move_direction_initial = ei->move_direction_initial;
4690         int move_pattern = ei->move_pattern;
4691
4692         if (move_direction_initial == MV_START_PREVIOUS)
4693         {
4694           if (MovDir[x][y] != MV_NONE)
4695             return;
4696
4697           move_direction_initial = MV_START_AUTOMATIC;
4698         }
4699
4700         if (move_direction_initial == MV_START_RANDOM)
4701           MovDir[x][y] = 1 << RND(4);
4702         else if (move_direction_initial & MV_ANY_DIRECTION)
4703           MovDir[x][y] = move_direction_initial;
4704         else if (move_pattern == MV_ALL_DIRECTIONS ||
4705                  move_pattern == MV_TURNING_LEFT ||
4706                  move_pattern == MV_TURNING_RIGHT ||
4707                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4708                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4709                  move_pattern == MV_TURNING_RANDOM)
4710           MovDir[x][y] = 1 << RND(4);
4711         else if (move_pattern == MV_HORIZONTAL)
4712           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4713         else if (move_pattern == MV_VERTICAL)
4714           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4715         else if (move_pattern & MV_ANY_DIRECTION)
4716           MovDir[x][y] = element_info[element].move_pattern;
4717         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4718                  move_pattern == MV_ALONG_RIGHT_SIDE)
4719         {
4720           /* use random direction as default start direction */
4721           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4722             MovDir[x][y] = 1 << RND(4);
4723
4724           for (i = 0; i < NUM_DIRECTIONS; i++)
4725           {
4726             int x1 = x + xy[i][0];
4727             int y1 = y + xy[i][1];
4728
4729             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4730             {
4731               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4732                 MovDir[x][y] = direction[0][i];
4733               else
4734                 MovDir[x][y] = direction[1][i];
4735
4736               break;
4737             }
4738           }
4739         }                
4740       }
4741       else
4742       {
4743         MovDir[x][y] = 1 << RND(4);
4744
4745         if (element != EL_BUG &&
4746             element != EL_SPACESHIP &&
4747             element != EL_BD_BUTTERFLY &&
4748             element != EL_BD_FIREFLY)
4749           break;
4750
4751         for (i = 0; i < NUM_DIRECTIONS; i++)
4752         {
4753           int x1 = x + xy[i][0];
4754           int y1 = y + xy[i][1];
4755
4756           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4757           {
4758             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4759             {
4760               MovDir[x][y] = direction[0][i];
4761               break;
4762             }
4763             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4764                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4765             {
4766               MovDir[x][y] = direction[1][i];
4767               break;
4768             }
4769           }
4770         }
4771       }
4772       break;
4773   }
4774
4775   GfxDir[x][y] = MovDir[x][y];
4776 }
4777
4778 void InitAmoebaNr(int x, int y)
4779 {
4780   int i;
4781   int group_nr = AmoebeNachbarNr(x, y);
4782
4783   if (group_nr == 0)
4784   {
4785     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4786     {
4787       if (AmoebaCnt[i] == 0)
4788       {
4789         group_nr = i;
4790         break;
4791       }
4792     }
4793   }
4794
4795   AmoebaNr[x][y] = group_nr;
4796   AmoebaCnt[group_nr]++;
4797   AmoebaCnt2[group_nr]++;
4798 }
4799
4800 static void PlayerWins(struct PlayerInfo *player)
4801 {
4802   player->LevelSolved = TRUE;
4803   player->GameOver = TRUE;
4804
4805   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4806                          level.native_em_level->lev->score : player->score);
4807
4808   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4809   player->LevelSolved_CountingScore = player->score_final;
4810 }
4811
4812 void GameWon()
4813 {
4814   static int time, time_final;
4815   static int score, score_final;
4816   static int game_over_delay_1 = 0;
4817   static int game_over_delay_2 = 0;
4818   int game_over_delay_value_1 = 50;
4819   int game_over_delay_value_2 = 50;
4820
4821   if (!local_player->LevelSolved_GameWon)
4822   {
4823     int i;
4824
4825     /* do not start end game actions before the player stops moving (to exit) */
4826     if (local_player->MovPos)
4827       return;
4828
4829     local_player->LevelSolved_GameWon = TRUE;
4830     local_player->LevelSolved_SaveTape = tape.recording;
4831     local_player->LevelSolved_SaveScore = !tape.playing;
4832
4833     if (tape.auto_play)         /* tape might already be stopped here */
4834       tape.auto_play_level_solved = TRUE;
4835
4836 #if 1
4837     TapeStop();
4838 #endif
4839
4840     game_over_delay_1 = game_over_delay_value_1;
4841     game_over_delay_2 = game_over_delay_value_2;
4842
4843     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4844     score = score_final = local_player->score_final;
4845
4846     if (TimeLeft > 0)
4847     {
4848       time_final = 0;
4849       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4850     }
4851     else if (level.time == 0 && TimePlayed < 999)
4852     {
4853       time_final = 999;
4854       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4855     }
4856
4857     local_player->score_final = score_final;
4858
4859     if (level_editor_test_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864 #if 1
4865       local_player->LevelSolved_CountingTime = time;
4866       local_player->LevelSolved_CountingScore = score;
4867
4868       game_panel_controls[GAME_PANEL_TIME].value = time;
4869       game_panel_controls[GAME_PANEL_SCORE].value = score;
4870
4871       DisplayGameControlValues();
4872 #else
4873       DrawGameValue_Time(time);
4874       DrawGameValue_Score(score);
4875 #endif
4876     }
4877
4878     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4879     {
4880       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4881       {
4882         /* close exit door after last player */
4883         if ((AllPlayersGone &&
4884              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4885               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4886               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4887             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4888             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4889         {
4890           int element = Feld[ExitX][ExitY];
4891
4892 #if 0
4893           if (element == EL_EM_EXIT_OPEN ||
4894               element == EL_EM_STEEL_EXIT_OPEN)
4895           {
4896             Bang(ExitX, ExitY);
4897           }
4898           else
4899 #endif
4900           {
4901             Feld[ExitX][ExitY] =
4902               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4903                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4904                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4905                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4906                EL_EM_STEEL_EXIT_CLOSING);
4907
4908             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4909           }
4910         }
4911
4912         /* player disappears */
4913         DrawLevelField(ExitX, ExitY);
4914       }
4915
4916       for (i = 0; i < MAX_PLAYERS; i++)
4917       {
4918         struct PlayerInfo *player = &stored_player[i];
4919
4920         if (player->present)
4921         {
4922           RemovePlayer(player);
4923
4924           /* player disappears */
4925           DrawLevelField(player->jx, player->jy);
4926         }
4927       }
4928     }
4929
4930     PlaySound(SND_GAME_WINNING);
4931   }
4932
4933   if (game_over_delay_1 > 0)
4934   {
4935     game_over_delay_1--;
4936
4937     return;
4938   }
4939
4940   if (time != time_final)
4941   {
4942     int time_to_go = ABS(time_final - time);
4943     int time_count_dir = (time < time_final ? +1 : -1);
4944     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4945
4946     time  += time_count_steps * time_count_dir;
4947     score += time_count_steps * level.score[SC_TIME_BONUS];
4948
4949 #if 1
4950     local_player->LevelSolved_CountingTime = time;
4951     local_player->LevelSolved_CountingScore = score;
4952
4953     game_panel_controls[GAME_PANEL_TIME].value = time;
4954     game_panel_controls[GAME_PANEL_SCORE].value = score;
4955
4956     DisplayGameControlValues();
4957 #else
4958     DrawGameValue_Time(time);
4959     DrawGameValue_Score(score);
4960 #endif
4961
4962     if (time == time_final)
4963       StopSound(SND_GAME_LEVELTIME_BONUS);
4964     else if (setup.sound_loops)
4965       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4966     else
4967       PlaySound(SND_GAME_LEVELTIME_BONUS);
4968
4969     return;
4970   }
4971
4972   local_player->LevelSolved_PanelOff = TRUE;
4973
4974   if (game_over_delay_2 > 0)
4975   {
4976     game_over_delay_2--;
4977
4978     return;
4979   }
4980
4981 #if 1
4982   GameEnd();
4983 #endif
4984 }
4985
4986 void GameEnd()
4987 {
4988   int hi_pos;
4989   boolean raise_level = FALSE;
4990
4991   local_player->LevelSolved_GameEnd = TRUE;
4992
4993   CloseDoor(DOOR_CLOSE_1);
4994
4995   if (local_player->LevelSolved_SaveTape)
4996   {
4997 #if 0
4998     TapeStop();
4999 #endif
5000
5001 #if 1
5002     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5003 #else
5004     SaveTape(tape.level_nr);            /* ask to save tape */
5005 #endif
5006   }
5007
5008   if (level_editor_test_game)
5009   {
5010     game_status = GAME_MODE_MAIN;
5011
5012 #if 1
5013     DrawAndFadeInMainMenu(REDRAW_FIELD);
5014 #else
5015     DrawMainMenu();
5016 #endif
5017
5018     return;
5019   }
5020
5021   if (!local_player->LevelSolved_SaveScore)
5022   {
5023 #if 1
5024     FadeOut(REDRAW_FIELD);
5025 #endif
5026
5027     game_status = GAME_MODE_MAIN;
5028
5029     DrawAndFadeInMainMenu(REDRAW_FIELD);
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037     SaveLevelSetup_SeriesInfo();
5038   }
5039
5040   if (level_nr < leveldir_current->last_level)
5041     raise_level = TRUE;                 /* advance to next level */
5042
5043   if ((hi_pos = NewHiScore()) >= 0) 
5044   {
5045     game_status = GAME_MODE_SCORES;
5046
5047     DrawHallOfFame(hi_pos);
5048
5049     if (raise_level)
5050     {
5051       level_nr++;
5052       TapeErase();
5053     }
5054   }
5055   else
5056   {
5057 #if 1
5058     FadeOut(REDRAW_FIELD);
5059 #endif
5060
5061     game_status = GAME_MODE_MAIN;
5062
5063     if (raise_level)
5064     {
5065       level_nr++;
5066       TapeErase();
5067     }
5068
5069     DrawAndFadeInMainMenu(REDRAW_FIELD);
5070   }
5071 }
5072
5073 int NewHiScore()
5074 {
5075   int k, l;
5076   int position = -1;
5077
5078   LoadScore(level_nr);
5079
5080   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5081       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5082     return -1;
5083
5084   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5085   {
5086     if (local_player->score_final > highscore[k].Score)
5087     {
5088       /* player has made it to the hall of fame */
5089
5090       if (k < MAX_SCORE_ENTRIES - 1)
5091       {
5092         int m = MAX_SCORE_ENTRIES - 1;
5093
5094 #ifdef ONE_PER_NAME
5095         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5096           if (strEqual(setup.player_name, highscore[l].Name))
5097             m = l;
5098         if (m == k)     /* player's new highscore overwrites his old one */
5099           goto put_into_list;
5100 #endif
5101
5102         for (l = m; l > k; l--)
5103         {
5104           strcpy(highscore[l].Name, highscore[l - 1].Name);
5105           highscore[l].Score = highscore[l - 1].Score;
5106         }
5107       }
5108
5109 #ifdef ONE_PER_NAME
5110       put_into_list:
5111 #endif
5112       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5113       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5114       highscore[k].Score = local_player->score_final; 
5115       position = k;
5116       break;
5117     }
5118
5119 #ifdef ONE_PER_NAME
5120     else if (!strncmp(setup.player_name, highscore[k].Name,
5121                       MAX_PLAYER_NAME_LEN))
5122       break;    /* player already there with a higher score */
5123 #endif
5124
5125   }
5126
5127   if (position >= 0) 
5128     SaveScore(level_nr);
5129
5130   return position;
5131 }
5132
5133 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5134 {
5135   int element = Feld[x][y];
5136   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5137   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5138   int horiz_move = (dx != 0);
5139   int sign = (horiz_move ? dx : dy);
5140   int step = sign * element_info[element].move_stepsize;
5141
5142   /* special values for move stepsize for spring and things on conveyor belt */
5143   if (horiz_move)
5144   {
5145     if (CAN_FALL(element) &&
5146         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5147       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5148     else if (element == EL_SPRING)
5149       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5150   }
5151
5152   return step;
5153 }
5154
5155 inline static int getElementMoveStepsize(int x, int y)
5156 {
5157   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5158 }
5159
5160 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5161 {
5162   if (player->GfxAction != action || player->GfxDir != dir)
5163   {
5164 #if 0
5165     printf("Player frame reset! (%d => %d, %d => %d)\n",
5166            player->GfxAction, action, player->GfxDir, dir);
5167 #endif
5168
5169     player->GfxAction = action;
5170     player->GfxDir = dir;
5171     player->Frame = 0;
5172     player->StepFrame = 0;
5173   }
5174 }
5175
5176 #if USE_GFX_RESET_GFX_ANIMATION
5177 static void ResetGfxFrame(int x, int y, boolean redraw)
5178 {
5179   int element = Feld[x][y];
5180   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5181   int last_gfx_frame = GfxFrame[x][y];
5182
5183   if (graphic_info[graphic].anim_global_sync)
5184     GfxFrame[x][y] = FrameCounter;
5185   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5186     GfxFrame[x][y] = CustomValue[x][y];
5187   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5188     GfxFrame[x][y] = element_info[element].collect_score;
5189   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5190     GfxFrame[x][y] = ChangeDelay[x][y];
5191
5192   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5193     DrawLevelGraphicAnimation(x, y, graphic);
5194 }
5195 #endif
5196
5197 static void ResetGfxAnimation(int x, int y)
5198 {
5199   GfxAction[x][y] = ACTION_DEFAULT;
5200   GfxDir[x][y] = MovDir[x][y];
5201   GfxFrame[x][y] = 0;
5202
5203 #if USE_GFX_RESET_GFX_ANIMATION
5204   ResetGfxFrame(x, y, FALSE);
5205 #endif
5206 }
5207
5208 static void ResetRandomAnimationValue(int x, int y)
5209 {
5210   GfxRandom[x][y] = INIT_GFX_RANDOM();
5211 }
5212
5213 void InitMovingField(int x, int y, int direction)
5214 {
5215   int element = Feld[x][y];
5216   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5217   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5218   int newx = x + dx;
5219   int newy = y + dy;
5220   boolean is_moving_before, is_moving_after;
5221 #if 0
5222   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5223 #endif
5224
5225   /* check if element was/is moving or being moved before/after mode change */
5226 #if 1
5227 #if 1
5228   is_moving_before = (WasJustMoving[x][y] != 0);
5229 #else
5230   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5231   is_moving_before = WasJustMoving[x][y];
5232 #endif
5233 #else
5234   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5235 #endif
5236   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5237
5238   /* reset animation only for moving elements which change direction of moving
5239      or which just started or stopped moving
5240      (else CEs with property "can move" / "not moving" are reset each frame) */
5241 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5242 #if 1
5243   if (is_moving_before != is_moving_after ||
5244       direction != MovDir[x][y])
5245     ResetGfxAnimation(x, y);
5246 #else
5247   if ((is_moving_before || is_moving_after) && !continues_moving)
5248     ResetGfxAnimation(x, y);
5249 #endif
5250 #else
5251   if (!continues_moving)
5252     ResetGfxAnimation(x, y);
5253 #endif
5254
5255   MovDir[x][y] = direction;
5256   GfxDir[x][y] = direction;
5257
5258 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5259   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5260                      direction == MV_DOWN && CAN_FALL(element) ?
5261                      ACTION_FALLING : ACTION_MOVING);
5262 #else
5263   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5264                      ACTION_FALLING : ACTION_MOVING);
5265 #endif
5266
5267   /* this is needed for CEs with property "can move" / "not moving" */
5268
5269   if (is_moving_after)
5270   {
5271     if (Feld[newx][newy] == EL_EMPTY)
5272       Feld[newx][newy] = EL_BLOCKED;
5273
5274     MovDir[newx][newy] = MovDir[x][y];
5275
5276 #if USE_NEW_CUSTOM_VALUE
5277     CustomValue[newx][newy] = CustomValue[x][y];
5278 #endif
5279
5280     GfxFrame[newx][newy] = GfxFrame[x][y];
5281     GfxRandom[newx][newy] = GfxRandom[x][y];
5282     GfxAction[newx][newy] = GfxAction[x][y];
5283     GfxDir[newx][newy] = GfxDir[x][y];
5284   }
5285 }
5286
5287 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5288 {
5289   int direction = MovDir[x][y];
5290   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5291   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5292
5293   *goes_to_x = newx;
5294   *goes_to_y = newy;
5295 }
5296
5297 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5298 {
5299   int oldx = x, oldy = y;
5300   int direction = MovDir[x][y];
5301
5302   if (direction == MV_LEFT)
5303     oldx++;
5304   else if (direction == MV_RIGHT)
5305     oldx--;
5306   else if (direction == MV_UP)
5307     oldy++;
5308   else if (direction == MV_DOWN)
5309     oldy--;
5310
5311   *comes_from_x = oldx;
5312   *comes_from_y = oldy;
5313 }
5314
5315 int MovingOrBlocked2Element(int x, int y)
5316 {
5317   int element = Feld[x][y];
5318
5319   if (element == EL_BLOCKED)
5320   {
5321     int oldx, oldy;
5322
5323     Blocked2Moving(x, y, &oldx, &oldy);
5324     return Feld[oldx][oldy];
5325   }
5326   else
5327     return element;
5328 }
5329
5330 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5331 {
5332   /* like MovingOrBlocked2Element(), but if element is moving
5333      and (x,y) is the field the moving element is just leaving,
5334      return EL_BLOCKED instead of the element value */
5335   int element = Feld[x][y];
5336
5337   if (IS_MOVING(x, y))
5338   {
5339     if (element == EL_BLOCKED)
5340     {
5341       int oldx, oldy;
5342
5343       Blocked2Moving(x, y, &oldx, &oldy);
5344       return Feld[oldx][oldy];
5345     }
5346     else
5347       return EL_BLOCKED;
5348   }
5349   else
5350     return element;
5351 }
5352
5353 static void RemoveField(int x, int y)
5354 {
5355   Feld[x][y] = EL_EMPTY;
5356
5357   MovPos[x][y] = 0;
5358   MovDir[x][y] = 0;
5359   MovDelay[x][y] = 0;
5360
5361 #if USE_NEW_CUSTOM_VALUE
5362   CustomValue[x][y] = 0;
5363 #endif
5364
5365   AmoebaNr[x][y] = 0;
5366   ChangeDelay[x][y] = 0;
5367   ChangePage[x][y] = -1;
5368   Pushed[x][y] = FALSE;
5369
5370 #if 0
5371   ExplodeField[x][y] = EX_TYPE_NONE;
5372 #endif
5373
5374   GfxElement[x][y] = EL_UNDEFINED;
5375   GfxAction[x][y] = ACTION_DEFAULT;
5376   GfxDir[x][y] = MV_NONE;
5377 #if 0
5378   /* !!! this would prevent the removed tile from being redrawn !!! */
5379   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5380 #endif
5381 }
5382
5383 void RemoveMovingField(int x, int y)
5384 {
5385   int oldx = x, oldy = y, newx = x, newy = y;
5386   int element = Feld[x][y];
5387   int next_element = EL_UNDEFINED;
5388
5389   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5390     return;
5391
5392   if (IS_MOVING(x, y))
5393   {
5394     Moving2Blocked(x, y, &newx, &newy);
5395
5396     if (Feld[newx][newy] != EL_BLOCKED)
5397     {
5398       /* element is moving, but target field is not free (blocked), but
5399          already occupied by something different (example: acid pool);
5400          in this case, only remove the moving field, but not the target */
5401
5402       RemoveField(oldx, oldy);
5403
5404       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5405
5406       TEST_DrawLevelField(oldx, oldy);
5407
5408       return;
5409     }
5410   }
5411   else if (element == EL_BLOCKED)
5412   {
5413     Blocked2Moving(x, y, &oldx, &oldy);
5414     if (!IS_MOVING(oldx, oldy))
5415       return;
5416   }
5417
5418   if (element == EL_BLOCKED &&
5419       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5420        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5421        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5422        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5423        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5424        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5425     next_element = get_next_element(Feld[oldx][oldy]);
5426
5427   RemoveField(oldx, oldy);
5428   RemoveField(newx, newy);
5429
5430   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5431
5432   if (next_element != EL_UNDEFINED)
5433     Feld[oldx][oldy] = next_element;
5434
5435   TEST_DrawLevelField(oldx, oldy);
5436   TEST_DrawLevelField(newx, newy);
5437 }
5438
5439 void DrawDynamite(int x, int y)
5440 {
5441   int sx = SCREENX(x), sy = SCREENY(y);
5442   int graphic = el2img(Feld[x][y]);
5443   int frame;
5444
5445   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5446     return;
5447
5448   if (IS_WALKABLE_INSIDE(Back[x][y]))
5449     return;
5450
5451   if (Back[x][y])
5452     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5453   else if (Store[x][y])
5454     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5455
5456   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5457
5458   if (Back[x][y] || Store[x][y])
5459     DrawGraphicThruMask(sx, sy, graphic, frame);
5460   else
5461     DrawGraphic(sx, sy, graphic, frame);
5462 }
5463
5464 void CheckDynamite(int x, int y)
5465 {
5466   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5467   {
5468     MovDelay[x][y]--;
5469
5470     if (MovDelay[x][y] != 0)
5471     {
5472       DrawDynamite(x, y);
5473       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5474
5475       return;
5476     }
5477   }
5478
5479   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5480
5481   Bang(x, y);
5482 }
5483
5484 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5485 {
5486   boolean num_checked_players = 0;
5487   int i;
5488
5489   for (i = 0; i < MAX_PLAYERS; i++)
5490   {
5491     if (stored_player[i].active)
5492     {
5493       int sx = stored_player[i].jx;
5494       int sy = stored_player[i].jy;
5495
5496       if (num_checked_players == 0)
5497       {
5498         *sx1 = *sx2 = sx;
5499         *sy1 = *sy2 = sy;
5500       }
5501       else
5502       {
5503         *sx1 = MIN(*sx1, sx);
5504         *sy1 = MIN(*sy1, sy);
5505         *sx2 = MAX(*sx2, sx);
5506         *sy2 = MAX(*sy2, sy);
5507       }
5508
5509       num_checked_players++;
5510     }
5511   }
5512 }
5513
5514 static boolean checkIfAllPlayersFitToScreen_RND()
5515 {
5516   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5517
5518   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5519
5520   return (sx2 - sx1 < SCR_FIELDX &&
5521           sy2 - sy1 < SCR_FIELDY);
5522 }
5523
5524 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5525 {
5526   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5527
5528   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5529
5530   *sx = (sx1 + sx2) / 2;
5531   *sy = (sy1 + sy2) / 2;
5532 }
5533
5534 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5535                         boolean center_screen, boolean quick_relocation)
5536 {
5537   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5538   boolean no_delay = (tape.warp_forward);
5539   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5540   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5541
5542   if (quick_relocation)
5543   {
5544     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5545     {
5546       if (!level.shifted_relocation || center_screen)
5547       {
5548         /* quick relocation (without scrolling), with centering of screen */
5549
5550         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5551                     x > SBX_Right + MIDPOSX ? SBX_Right :
5552                     x - MIDPOSX);
5553
5554         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5555                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5556                     y - MIDPOSY);
5557       }
5558       else
5559       {
5560         /* quick relocation (without scrolling), but do not center screen */
5561
5562         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5563                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5564                                old_x - MIDPOSX);
5565
5566         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5567                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5568                                old_y - MIDPOSY);
5569
5570         int offset_x = x + (scroll_x - center_scroll_x);
5571         int offset_y = y + (scroll_y - center_scroll_y);
5572
5573         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5574                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5575                     offset_x - MIDPOSX);
5576
5577         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5578                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5579                     offset_y - MIDPOSY);
5580       }
5581     }
5582     else
5583     {
5584 #if 1
5585       if (!level.shifted_relocation || center_screen)
5586       {
5587         /* quick relocation (without scrolling), with centering of screen */
5588
5589         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5590                     x > SBX_Right + MIDPOSX ? SBX_Right :
5591                     x - MIDPOSX);
5592
5593         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5595                     y - MIDPOSY);
5596       }
5597       else
5598       {
5599         /* quick relocation (without scrolling), but do not center screen */
5600
5601         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5602                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5603                                old_x - MIDPOSX);
5604
5605         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5606                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5607                                old_y - MIDPOSY);
5608
5609         int offset_x = x + (scroll_x - center_scroll_x);
5610         int offset_y = y + (scroll_y - center_scroll_y);
5611
5612         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5613                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5614                     offset_x - MIDPOSX);
5615
5616         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5617                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5618                     offset_y - MIDPOSY);
5619       }
5620 #else
5621       /* quick relocation (without scrolling), inside visible screen area */
5622
5623       int offset = game.scroll_delay_value;
5624
5625       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5626           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5627         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5628
5629       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5630           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5631         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5632
5633       /* don't scroll over playfield boundaries */
5634       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5635         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5636
5637       /* don't scroll over playfield boundaries */
5638       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5639         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5640 #endif
5641     }
5642
5643     RedrawPlayfield(TRUE, 0,0,0,0);
5644   }
5645   else
5646   {
5647 #if 1
5648     int scroll_xx, scroll_yy;
5649
5650     if (!level.shifted_relocation || center_screen)
5651     {
5652       /* visible relocation (with scrolling), with centering of screen */
5653
5654       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5655                    x > SBX_Right + MIDPOSX ? SBX_Right :
5656                    x - MIDPOSX);
5657
5658       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5659                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5660                    y - MIDPOSY);
5661     }
5662     else
5663     {
5664       /* visible relocation (with scrolling), but do not center screen */
5665
5666       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5667                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5668                              old_x - MIDPOSX);
5669
5670       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5671                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5672                              old_y - MIDPOSY);
5673
5674       int offset_x = x + (scroll_x - center_scroll_x);
5675       int offset_y = y + (scroll_y - center_scroll_y);
5676
5677       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5678                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5679                    offset_x - MIDPOSX);
5680
5681       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5682                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5683                    offset_y - MIDPOSY);
5684     }
5685
5686 #else
5687
5688     /* visible relocation (with scrolling), with centering of screen */
5689
5690     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5691                      x > SBX_Right + MIDPOSX ? SBX_Right :
5692                      x - MIDPOSX);
5693
5694     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5695                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5696                      y - MIDPOSY);
5697 #endif
5698
5699     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5700
5701     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5702     {
5703       int dx = 0, dy = 0;
5704       int fx = FX, fy = FY;
5705
5706       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5707       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5708
5709       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5710         break;
5711
5712       scroll_x -= dx;
5713       scroll_y -= dy;
5714
5715       fx += dx * TILEX / 2;
5716       fy += dy * TILEY / 2;
5717
5718       ScrollLevel(dx, dy);
5719       DrawAllPlayers();
5720
5721       /* scroll in two steps of half tile size to make things smoother */
5722       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5723       FlushDisplay();
5724       Delay(wait_delay_value);
5725
5726       /* scroll second step to align at full tile size */
5727       BackToFront();
5728       Delay(wait_delay_value);
5729     }
5730
5731     DrawAllPlayers();
5732     BackToFront();
5733     Delay(wait_delay_value);
5734   }
5735 }
5736
5737 void RelocatePlayer(int jx, int jy, int el_player_raw)
5738 {
5739   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5740   int player_nr = GET_PLAYER_NR(el_player);
5741   struct PlayerInfo *player = &stored_player[player_nr];
5742   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5743   boolean no_delay = (tape.warp_forward);
5744   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5745   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5746   int old_jx = player->jx;
5747   int old_jy = player->jy;
5748   int old_element = Feld[old_jx][old_jy];
5749   int element = Feld[jx][jy];
5750   boolean player_relocated = (old_jx != jx || old_jy != jy);
5751
5752   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5753   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5754   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5755   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5756   int leave_side_horiz = move_dir_horiz;
5757   int leave_side_vert  = move_dir_vert;
5758   int enter_side = enter_side_horiz | enter_side_vert;
5759   int leave_side = leave_side_horiz | leave_side_vert;
5760
5761   if (player->GameOver)         /* do not reanimate dead player */
5762     return;
5763
5764   if (!player_relocated)        /* no need to relocate the player */
5765     return;
5766
5767   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5768   {
5769     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5770     DrawLevelField(jx, jy);
5771   }
5772
5773   if (player->present)
5774   {
5775     while (player->MovPos)
5776     {
5777       ScrollPlayer(player, SCROLL_GO_ON);
5778       ScrollScreen(NULL, SCROLL_GO_ON);
5779
5780       AdvanceFrameAndPlayerCounters(player->index_nr);
5781
5782       DrawPlayer(player);
5783
5784       BackToFront();
5785       Delay(wait_delay_value);
5786     }
5787
5788     DrawPlayer(player);         /* needed here only to cleanup last field */
5789     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5790
5791     player->is_moving = FALSE;
5792   }
5793
5794   if (IS_CUSTOM_ELEMENT(old_element))
5795     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5796                                CE_LEFT_BY_PLAYER,
5797                                player->index_bit, leave_side);
5798
5799   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5800                                       CE_PLAYER_LEAVES_X,
5801                                       player->index_bit, leave_side);
5802
5803   Feld[jx][jy] = el_player;
5804   InitPlayerField(jx, jy, el_player, TRUE);
5805
5806   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5807      possible that the relocation target field did not contain a player element,
5808      but a walkable element, to which the new player was relocated -- in this
5809      case, restore that (already initialized!) element on the player field */
5810   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5811   {
5812     Feld[jx][jy] = element;     /* restore previously existing element */
5813 #if 0
5814     /* !!! do not initialize already initialized element a second time !!! */
5815     /* (this causes at least problems with "element creation" CE trigger for
5816        already existing elements, and existing Sokoban fields counted twice) */
5817     InitField(jx, jy, FALSE);
5818 #endif
5819   }
5820
5821   /* only visually relocate centered player */
5822   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5823                      FALSE, level.instant_relocation);
5824
5825   TestIfPlayerTouchesBadThing(jx, jy);
5826   TestIfPlayerTouchesCustomElement(jx, jy);
5827
5828   if (IS_CUSTOM_ELEMENT(element))
5829     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5830                                player->index_bit, enter_side);
5831
5832   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5833                                       player->index_bit, enter_side);
5834
5835 #if 1
5836   if (player->is_switching)
5837   {
5838     /* ensure that relocation while still switching an element does not cause
5839        a new element to be treated as also switched directly after relocation
5840        (this is important for teleporter switches that teleport the player to
5841        a place where another teleporter switch is in the same direction, which
5842        would then incorrectly be treated as immediately switched before the
5843        direction key that caused the switch was released) */
5844
5845     player->switch_x += jx - old_jx;
5846     player->switch_y += jy - old_jy;
5847   }
5848 #endif
5849 }
5850
5851 void Explode(int ex, int ey, int phase, int mode)
5852 {
5853   int x, y;
5854   int last_phase;
5855   int border_element;
5856
5857   /* !!! eliminate this variable !!! */
5858   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5859
5860   if (game.explosions_delayed)
5861   {
5862     ExplodeField[ex][ey] = mode;
5863     return;
5864   }
5865
5866   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5867   {
5868     int center_element = Feld[ex][ey];
5869     int artwork_element, explosion_element;     /* set these values later */
5870
5871 #if 0
5872     /* --- This is only really needed (and now handled) in "Impact()". --- */
5873     /* do not explode moving elements that left the explode field in time */
5874     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5875         center_element == EL_EMPTY &&
5876         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5877       return;
5878 #endif
5879
5880 #if 0
5881     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5882     if (mode == EX_TYPE_NORMAL ||
5883         mode == EX_TYPE_CENTER ||
5884         mode == EX_TYPE_CROSS)
5885       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5886 #endif
5887
5888     /* remove things displayed in background while burning dynamite */
5889     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5890       Back[ex][ey] = 0;
5891
5892     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5893     {
5894       /* put moving element to center field (and let it explode there) */
5895       center_element = MovingOrBlocked2Element(ex, ey);
5896       RemoveMovingField(ex, ey);
5897       Feld[ex][ey] = center_element;
5898     }
5899
5900     /* now "center_element" is finally determined -- set related values now */
5901     artwork_element = center_element;           /* for custom player artwork */
5902     explosion_element = center_element;         /* for custom player artwork */
5903
5904     if (IS_PLAYER(ex, ey))
5905     {
5906       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5907
5908       artwork_element = stored_player[player_nr].artwork_element;
5909
5910       if (level.use_explosion_element[player_nr])
5911       {
5912         explosion_element = level.explosion_element[player_nr];
5913         artwork_element = explosion_element;
5914       }
5915     }
5916
5917 #if 1
5918     if (mode == EX_TYPE_NORMAL ||
5919         mode == EX_TYPE_CENTER ||
5920         mode == EX_TYPE_CROSS)
5921       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5922 #endif
5923
5924     last_phase = element_info[explosion_element].explosion_delay + 1;
5925
5926     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5927     {
5928       int xx = x - ex + 1;
5929       int yy = y - ey + 1;
5930       int element;
5931
5932       if (!IN_LEV_FIELD(x, y) ||
5933           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5934           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5935         continue;
5936
5937       element = Feld[x][y];
5938
5939       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5940       {
5941         element = MovingOrBlocked2Element(x, y);
5942
5943         if (!IS_EXPLOSION_PROOF(element))
5944           RemoveMovingField(x, y);
5945       }
5946
5947       /* indestructible elements can only explode in center (but not flames) */
5948       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5949                                            mode == EX_TYPE_BORDER)) ||
5950           element == EL_FLAMES)
5951         continue;
5952
5953       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5954          behaviour, for example when touching a yamyam that explodes to rocks
5955          with active deadly shield, a rock is created under the player !!! */
5956       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5957 #if 0
5958       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5959           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5960            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5961 #else
5962       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5963 #endif
5964       {
5965         if (IS_ACTIVE_BOMB(element))
5966         {
5967           /* re-activate things under the bomb like gate or penguin */
5968           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5969           Back[x][y] = 0;
5970         }
5971
5972         continue;
5973       }
5974
5975       /* save walkable background elements while explosion on same tile */
5976       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5977           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5978         Back[x][y] = element;
5979
5980       /* ignite explodable elements reached by other explosion */
5981       if (element == EL_EXPLOSION)
5982         element = Store2[x][y];
5983
5984       if (AmoebaNr[x][y] &&
5985           (element == EL_AMOEBA_FULL ||
5986            element == EL_BD_AMOEBA ||
5987            element == EL_AMOEBA_GROWING))
5988       {
5989         AmoebaCnt[AmoebaNr[x][y]]--;
5990         AmoebaCnt2[AmoebaNr[x][y]]--;
5991       }
5992
5993       RemoveField(x, y);
5994
5995       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5996       {
5997         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5998
5999         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6000
6001         if (PLAYERINFO(ex, ey)->use_murphy)
6002           Store[x][y] = EL_EMPTY;
6003       }
6004
6005       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6006          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6007       else if (ELEM_IS_PLAYER(center_element))
6008         Store[x][y] = EL_EMPTY;
6009       else if (center_element == EL_YAMYAM)
6010         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6011       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6012         Store[x][y] = element_info[center_element].content.e[xx][yy];
6013 #if 1
6014       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6015          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6016          otherwise) -- FIX THIS !!! */
6017       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6018         Store[x][y] = element_info[element].content.e[1][1];
6019 #else
6020       else if (!CAN_EXPLODE(element))
6021         Store[x][y] = element_info[element].content.e[1][1];
6022 #endif
6023       else
6024         Store[x][y] = EL_EMPTY;
6025
6026       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6027           center_element == EL_AMOEBA_TO_DIAMOND)
6028         Store2[x][y] = element;
6029
6030       Feld[x][y] = EL_EXPLOSION;
6031       GfxElement[x][y] = artwork_element;
6032
6033       ExplodePhase[x][y] = 1;
6034       ExplodeDelay[x][y] = last_phase;
6035
6036       Stop[x][y] = TRUE;
6037     }
6038
6039     if (center_element == EL_YAMYAM)
6040       game.yamyam_content_nr =
6041         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6042
6043     return;
6044   }
6045
6046   if (Stop[ex][ey])
6047     return;
6048
6049   x = ex;
6050   y = ey;
6051
6052   if (phase == 1)
6053     GfxFrame[x][y] = 0;         /* restart explosion animation */
6054
6055   last_phase = ExplodeDelay[x][y];
6056
6057   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6058
6059 #ifdef DEBUG
6060
6061   /* activate this even in non-DEBUG version until cause for crash in
6062      getGraphicAnimationFrame() (see below) is found and eliminated */
6063
6064 #endif
6065 #if 1
6066
6067 #if 1
6068   /* this can happen if the player leaves an explosion just in time */
6069   if (GfxElement[x][y] == EL_UNDEFINED)
6070     GfxElement[x][y] = EL_EMPTY;
6071 #else
6072   if (GfxElement[x][y] == EL_UNDEFINED)
6073   {
6074     printf("\n\n");
6075     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6076     printf("Explode(): This should never happen!\n");
6077     printf("\n\n");
6078
6079     GfxElement[x][y] = EL_EMPTY;
6080   }
6081 #endif
6082
6083 #endif
6084
6085   border_element = Store2[x][y];
6086   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6087     border_element = StorePlayer[x][y];
6088
6089   if (phase == element_info[border_element].ignition_delay ||
6090       phase == last_phase)
6091   {
6092     boolean border_explosion = FALSE;
6093
6094     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6095         !PLAYER_EXPLOSION_PROTECTED(x, y))
6096     {
6097       KillPlayerUnlessExplosionProtected(x, y);
6098       border_explosion = TRUE;
6099     }
6100     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6101     {
6102       Feld[x][y] = Store2[x][y];
6103       Store2[x][y] = 0;
6104       Bang(x, y);
6105       border_explosion = TRUE;
6106     }
6107     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6108     {
6109       AmoebeUmwandeln(x, y);
6110       Store2[x][y] = 0;
6111       border_explosion = TRUE;
6112     }
6113
6114     /* if an element just explodes due to another explosion (chain-reaction),
6115        do not immediately end the new explosion when it was the last frame of
6116        the explosion (as it would be done in the following "if"-statement!) */
6117     if (border_explosion && phase == last_phase)
6118       return;
6119   }
6120
6121   if (phase == last_phase)
6122   {
6123     int element;
6124
6125     element = Feld[x][y] = Store[x][y];
6126     Store[x][y] = Store2[x][y] = 0;
6127     GfxElement[x][y] = EL_UNDEFINED;
6128
6129     /* player can escape from explosions and might therefore be still alive */
6130     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6131         element <= EL_PLAYER_IS_EXPLODING_4)
6132     {
6133       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6134       int explosion_element = EL_PLAYER_1 + player_nr;
6135       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6136       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6137
6138       if (level.use_explosion_element[player_nr])
6139         explosion_element = level.explosion_element[player_nr];
6140
6141       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6142                     element_info[explosion_element].content.e[xx][yy]);
6143     }
6144
6145     /* restore probably existing indestructible background element */
6146     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6147       element = Feld[x][y] = Back[x][y];
6148     Back[x][y] = 0;
6149
6150     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6151     GfxDir[x][y] = MV_NONE;
6152     ChangeDelay[x][y] = 0;
6153     ChangePage[x][y] = -1;
6154
6155 #if USE_NEW_CUSTOM_VALUE
6156     CustomValue[x][y] = 0;
6157 #endif
6158
6159     InitField_WithBug2(x, y, FALSE);
6160
6161     TEST_DrawLevelField(x, y);
6162
6163     TestIfElementTouchesCustomElement(x, y);
6164
6165     if (GFX_CRUMBLED(element))
6166       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6167
6168     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6169       StorePlayer[x][y] = 0;
6170
6171     if (ELEM_IS_PLAYER(element))
6172       RelocatePlayer(x, y, element);
6173   }
6174   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6175   {
6176     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6177     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6178
6179     if (phase == delay)
6180       TEST_DrawLevelFieldCrumbled(x, y);
6181
6182     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6183     {
6184       DrawLevelElement(x, y, Back[x][y]);
6185       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6186     }
6187     else if (IS_WALKABLE_UNDER(Back[x][y]))
6188     {
6189       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6190       DrawLevelElementThruMask(x, y, Back[x][y]);
6191     }
6192     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6193       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6194   }
6195 }
6196
6197 void DynaExplode(int ex, int ey)
6198 {
6199   int i, j;
6200   int dynabomb_element = Feld[ex][ey];
6201   int dynabomb_size = 1;
6202   boolean dynabomb_xl = FALSE;
6203   struct PlayerInfo *player;
6204   static int xy[4][2] =
6205   {
6206     { 0, -1 },
6207     { -1, 0 },
6208     { +1, 0 },
6209     { 0, +1 }
6210   };
6211
6212   if (IS_ACTIVE_BOMB(dynabomb_element))
6213   {
6214     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6215     dynabomb_size = player->dynabomb_size;
6216     dynabomb_xl = player->dynabomb_xl;
6217     player->dynabombs_left++;
6218   }
6219
6220   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6221
6222   for (i = 0; i < NUM_DIRECTIONS; i++)
6223   {
6224     for (j = 1; j <= dynabomb_size; j++)
6225     {
6226       int x = ex + j * xy[i][0];
6227       int y = ey + j * xy[i][1];
6228       int element;
6229
6230       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6231         break;
6232
6233       element = Feld[x][y];
6234
6235       /* do not restart explosions of fields with active bombs */
6236       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6237         continue;
6238
6239       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6240
6241       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6242           !IS_DIGGABLE(element) && !dynabomb_xl)
6243         break;
6244     }
6245   }
6246 }
6247
6248 void Bang(int x, int y)
6249 {
6250   int element = MovingOrBlocked2Element(x, y);
6251   int explosion_type = EX_TYPE_NORMAL;
6252
6253   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6254   {
6255     struct PlayerInfo *player = PLAYERINFO(x, y);
6256
6257 #if USE_FIX_CE_ACTION_WITH_PLAYER
6258     element = Feld[x][y] = player->initial_element;
6259 #else
6260     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6261                             player->element_nr);
6262 #endif
6263
6264     if (level.use_explosion_element[player->index_nr])
6265     {
6266       int explosion_element = level.explosion_element[player->index_nr];
6267
6268       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6269         explosion_type = EX_TYPE_CROSS;
6270       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6271         explosion_type = EX_TYPE_CENTER;
6272     }
6273   }
6274
6275   switch (element)
6276   {
6277     case EL_BUG:
6278     case EL_SPACESHIP:
6279     case EL_BD_BUTTERFLY:
6280     case EL_BD_FIREFLY:
6281     case EL_YAMYAM:
6282     case EL_DARK_YAMYAM:
6283     case EL_ROBOT:
6284     case EL_PACMAN:
6285     case EL_MOLE:
6286       RaiseScoreElement(element);
6287       break;
6288
6289     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6290     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6291     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6292     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6293     case EL_DYNABOMB_INCREASE_NUMBER:
6294     case EL_DYNABOMB_INCREASE_SIZE:
6295     case EL_DYNABOMB_INCREASE_POWER:
6296       explosion_type = EX_TYPE_DYNA;
6297       break;
6298
6299     case EL_DC_LANDMINE:
6300 #if 0
6301     case EL_EM_EXIT_OPEN:
6302     case EL_EM_STEEL_EXIT_OPEN:
6303 #endif
6304       explosion_type = EX_TYPE_CENTER;
6305       break;
6306
6307     case EL_PENGUIN:
6308     case EL_LAMP:
6309     case EL_LAMP_ACTIVE:
6310     case EL_AMOEBA_TO_DIAMOND:
6311       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6312         explosion_type = EX_TYPE_CENTER;
6313       break;
6314
6315     default:
6316       if (element_info[element].explosion_type == EXPLODES_CROSS)
6317         explosion_type = EX_TYPE_CROSS;
6318       else if (element_info[element].explosion_type == EXPLODES_1X1)
6319         explosion_type = EX_TYPE_CENTER;
6320       break;
6321   }
6322
6323   if (explosion_type == EX_TYPE_DYNA)
6324     DynaExplode(x, y);
6325   else
6326     Explode(x, y, EX_PHASE_START, explosion_type);
6327
6328   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6329 }
6330
6331 void SplashAcid(int x, int y)
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_LEFT;
6337
6338   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6339       (!IN_LEV_FIELD(x + 1, y - 2) ||
6340        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6341     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6342
6343   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6344 }
6345
6346 static void InitBeltMovement()
6347 {
6348   static int belt_base_element[4] =
6349   {
6350     EL_CONVEYOR_BELT_1_LEFT,
6351     EL_CONVEYOR_BELT_2_LEFT,
6352     EL_CONVEYOR_BELT_3_LEFT,
6353     EL_CONVEYOR_BELT_4_LEFT
6354   };
6355   static int belt_base_active_element[4] =
6356   {
6357     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6358     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6359     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6360     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6361   };
6362
6363   int x, y, i, j;
6364
6365   /* set frame order for belt animation graphic according to belt direction */
6366   for (i = 0; i < NUM_BELTS; i++)
6367   {
6368     int belt_nr = i;
6369
6370     for (j = 0; j < NUM_BELT_PARTS; j++)
6371     {
6372       int element = belt_base_active_element[belt_nr] + j;
6373       int graphic_1 = el2img(element);
6374       int graphic_2 = el2panelimg(element);
6375
6376       if (game.belt_dir[i] == MV_LEFT)
6377       {
6378         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6379         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6380       }
6381       else
6382       {
6383         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6384         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6385       }
6386     }
6387   }
6388
6389   SCAN_PLAYFIELD(x, y)
6390   {
6391     int element = Feld[x][y];
6392
6393     for (i = 0; i < NUM_BELTS; i++)
6394     {
6395       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6396       {
6397         int e_belt_nr = getBeltNrFromBeltElement(element);
6398         int belt_nr = i;
6399
6400         if (e_belt_nr == belt_nr)
6401         {
6402           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6403
6404           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6405         }
6406       }
6407     }
6408   }
6409 }
6410
6411 static void ToggleBeltSwitch(int x, int y)
6412 {
6413   static int belt_base_element[4] =
6414   {
6415     EL_CONVEYOR_BELT_1_LEFT,
6416     EL_CONVEYOR_BELT_2_LEFT,
6417     EL_CONVEYOR_BELT_3_LEFT,
6418     EL_CONVEYOR_BELT_4_LEFT
6419   };
6420   static int belt_base_active_element[4] =
6421   {
6422     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6423     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6424     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6425     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6426   };
6427   static int belt_base_switch_element[4] =
6428   {
6429     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6430     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6431     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6432     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6433   };
6434   static int belt_move_dir[4] =
6435   {
6436     MV_LEFT,
6437     MV_NONE,
6438     MV_RIGHT,
6439     MV_NONE,
6440   };
6441
6442   int element = Feld[x][y];
6443   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6444   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6445   int belt_dir = belt_move_dir[belt_dir_nr];
6446   int xx, yy, i;
6447
6448   if (!IS_BELT_SWITCH(element))
6449     return;
6450
6451   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6452   game.belt_dir[belt_nr] = belt_dir;
6453
6454   if (belt_dir_nr == 3)
6455     belt_dir_nr = 1;
6456
6457   /* set frame order for belt animation graphic according to belt direction */
6458   for (i = 0; i < NUM_BELT_PARTS; i++)
6459   {
6460     int element = belt_base_active_element[belt_nr] + i;
6461     int graphic_1 = el2img(element);
6462     int graphic_2 = el2panelimg(element);
6463
6464     if (belt_dir == MV_LEFT)
6465     {
6466       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6467       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6468     }
6469     else
6470     {
6471       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6472       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(xx, yy)
6477   {
6478     int element = Feld[xx][yy];
6479
6480     if (IS_BELT_SWITCH(element))
6481     {
6482       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6483
6484       if (e_belt_nr == belt_nr)
6485       {
6486         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6487         TEST_DrawLevelField(xx, yy);
6488       }
6489     }
6490     else if (IS_BELT(element) && belt_dir != MV_NONE)
6491     {
6492       int e_belt_nr = getBeltNrFromBeltElement(element);
6493
6494       if (e_belt_nr == belt_nr)
6495       {
6496         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6497
6498         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6499         TEST_DrawLevelField(xx, yy);
6500       }
6501     }
6502     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6503     {
6504       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6505
6506       if (e_belt_nr == belt_nr)
6507       {
6508         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6509
6510         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6511         TEST_DrawLevelField(xx, yy);
6512       }
6513     }
6514   }
6515 }
6516
6517 static void ToggleSwitchgateSwitch(int x, int y)
6518 {
6519   int xx, yy;
6520
6521   game.switchgate_pos = !game.switchgate_pos;
6522
6523   SCAN_PLAYFIELD(xx, yy)
6524   {
6525     int element = Feld[xx][yy];
6526
6527 #if !USE_BOTH_SWITCHGATE_SWITCHES
6528     if (element == EL_SWITCHGATE_SWITCH_UP ||
6529         element == EL_SWITCHGATE_SWITCH_DOWN)
6530     {
6531       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6532       TEST_DrawLevelField(xx, yy);
6533     }
6534     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6535              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6536     {
6537       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6538       TEST_DrawLevelField(xx, yy);
6539     }
6540 #else
6541     if (element == EL_SWITCHGATE_SWITCH_UP)
6542     {
6543       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6544       TEST_DrawLevelField(xx, yy);
6545     }
6546     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6547     {
6548       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6549       TEST_DrawLevelField(xx, yy);
6550     }
6551     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6552     {
6553       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6554       TEST_DrawLevelField(xx, yy);
6555     }
6556     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6557     {
6558       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6559       TEST_DrawLevelField(xx, yy);
6560     }
6561 #endif
6562     else if (element == EL_SWITCHGATE_OPEN ||
6563              element == EL_SWITCHGATE_OPENING)
6564     {
6565       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6566
6567       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6568     }
6569     else if (element == EL_SWITCHGATE_CLOSED ||
6570              element == EL_SWITCHGATE_CLOSING)
6571     {
6572       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6573
6574       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6575     }
6576   }
6577 }
6578
6579 static int getInvisibleActiveFromInvisibleElement(int element)
6580 {
6581   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6582           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6583           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6584           element);
6585 }
6586
6587 static int getInvisibleFromInvisibleActiveElement(int element)
6588 {
6589   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6590           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6591           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6592           element);
6593 }
6594
6595 static void RedrawAllLightSwitchesAndInvisibleElements()
6596 {
6597   int x, y;
6598
6599   SCAN_PLAYFIELD(x, y)
6600   {
6601     int element = Feld[x][y];
6602
6603     if (element == EL_LIGHT_SWITCH &&
6604         game.light_time_left > 0)
6605     {
6606       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6607       TEST_DrawLevelField(x, y);
6608     }
6609     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6610              game.light_time_left == 0)
6611     {
6612       Feld[x][y] = EL_LIGHT_SWITCH;
6613       TEST_DrawLevelField(x, y);
6614     }
6615     else if (element == EL_EMC_DRIPPER &&
6616              game.light_time_left > 0)
6617     {
6618       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6619       TEST_DrawLevelField(x, y);
6620     }
6621     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6622              game.light_time_left == 0)
6623     {
6624       Feld[x][y] = EL_EMC_DRIPPER;
6625       TEST_DrawLevelField(x, y);
6626     }
6627     else if (element == EL_INVISIBLE_STEELWALL ||
6628              element == EL_INVISIBLE_WALL ||
6629              element == EL_INVISIBLE_SAND)
6630     {
6631       if (game.light_time_left > 0)
6632         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6633
6634       TEST_DrawLevelField(x, y);
6635
6636       /* uncrumble neighbour fields, if needed */
6637       if (element == EL_INVISIBLE_SAND)
6638         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6639     }
6640     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6641              element == EL_INVISIBLE_WALL_ACTIVE ||
6642              element == EL_INVISIBLE_SAND_ACTIVE)
6643     {
6644       if (game.light_time_left == 0)
6645         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6646
6647       TEST_DrawLevelField(x, y);
6648
6649       /* re-crumble neighbour fields, if needed */
6650       if (element == EL_INVISIBLE_SAND)
6651         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6652     }
6653   }
6654 }
6655
6656 static void RedrawAllInvisibleElementsForLenses()
6657 {
6658   int x, y;
6659
6660   SCAN_PLAYFIELD(x, y)
6661   {
6662     int element = Feld[x][y];
6663
6664     if (element == EL_EMC_DRIPPER &&
6665         game.lenses_time_left > 0)
6666     {
6667       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6668       TEST_DrawLevelField(x, y);
6669     }
6670     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6671              game.lenses_time_left == 0)
6672     {
6673       Feld[x][y] = EL_EMC_DRIPPER;
6674       TEST_DrawLevelField(x, y);
6675     }
6676     else if (element == EL_INVISIBLE_STEELWALL ||
6677              element == EL_INVISIBLE_WALL ||
6678              element == EL_INVISIBLE_SAND)
6679     {
6680       if (game.lenses_time_left > 0)
6681         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6682
6683       TEST_DrawLevelField(x, y);
6684
6685       /* uncrumble neighbour fields, if needed */
6686       if (element == EL_INVISIBLE_SAND)
6687         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6688     }
6689     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6690              element == EL_INVISIBLE_WALL_ACTIVE ||
6691              element == EL_INVISIBLE_SAND_ACTIVE)
6692     {
6693       if (game.lenses_time_left == 0)
6694         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6695
6696       TEST_DrawLevelField(x, y);
6697
6698       /* re-crumble neighbour fields, if needed */
6699       if (element == EL_INVISIBLE_SAND)
6700         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6701     }
6702   }
6703 }
6704
6705 static void RedrawAllInvisibleElementsForMagnifier()
6706 {
6707   int x, y;
6708
6709   SCAN_PLAYFIELD(x, y)
6710   {
6711     int element = Feld[x][y];
6712
6713     if (element == EL_EMC_FAKE_GRASS &&
6714         game.magnify_time_left > 0)
6715     {
6716       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6717       TEST_DrawLevelField(x, y);
6718     }
6719     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6720              game.magnify_time_left == 0)
6721     {
6722       Feld[x][y] = EL_EMC_FAKE_GRASS;
6723       TEST_DrawLevelField(x, y);
6724     }
6725     else if (IS_GATE_GRAY(element) &&
6726              game.magnify_time_left > 0)
6727     {
6728       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6729                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6730                     IS_EM_GATE_GRAY(element) ?
6731                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6732                     IS_EMC_GATE_GRAY(element) ?
6733                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6734                     IS_DC_GATE_GRAY(element) ?
6735                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6736                     element);
6737       TEST_DrawLevelField(x, y);
6738     }
6739     else if (IS_GATE_GRAY_ACTIVE(element) &&
6740              game.magnify_time_left == 0)
6741     {
6742       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6743                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6744                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6745                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6746                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6747                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6748                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6749                     EL_DC_GATE_WHITE_GRAY :
6750                     element);
6751       TEST_DrawLevelField(x, y);
6752     }
6753   }
6754 }
6755
6756 static void ToggleLightSwitch(int x, int y)
6757 {
6758   int element = Feld[x][y];
6759
6760   game.light_time_left =
6761     (element == EL_LIGHT_SWITCH ?
6762      level.time_light * FRAMES_PER_SECOND : 0);
6763
6764   RedrawAllLightSwitchesAndInvisibleElements();
6765 }
6766
6767 static void ActivateTimegateSwitch(int x, int y)
6768 {
6769   int xx, yy;
6770
6771   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6772
6773   SCAN_PLAYFIELD(xx, yy)
6774   {
6775     int element = Feld[xx][yy];
6776
6777     if (element == EL_TIMEGATE_CLOSED ||
6778         element == EL_TIMEGATE_CLOSING)
6779     {
6780       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6781       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6782     }
6783
6784     /*
6785     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6786     {
6787       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6788       TEST_DrawLevelField(xx, yy);
6789     }
6790     */
6791
6792   }
6793
6794 #if 1
6795   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6796                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6797 #else
6798   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6799 #endif
6800 }
6801
6802 void Impact(int x, int y)
6803 {
6804   boolean last_line = (y == lev_fieldy - 1);
6805   boolean object_hit = FALSE;
6806   boolean impact = (last_line || object_hit);
6807   int element = Feld[x][y];
6808   int smashed = EL_STEELWALL;
6809
6810   if (!last_line)       /* check if element below was hit */
6811   {
6812     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6813       return;
6814
6815     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6816                                          MovDir[x][y + 1] != MV_DOWN ||
6817                                          MovPos[x][y + 1] <= TILEY / 2));
6818
6819     /* do not smash moving elements that left the smashed field in time */
6820     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6821         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6822       object_hit = FALSE;
6823
6824 #if USE_QUICKSAND_IMPACT_BUGFIX
6825     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6826     {
6827       RemoveMovingField(x, y + 1);
6828       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6829       Feld[x][y + 2] = EL_ROCK;
6830       TEST_DrawLevelField(x, y + 2);
6831
6832       object_hit = TRUE;
6833     }
6834
6835     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6836     {
6837       RemoveMovingField(x, y + 1);
6838       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6839       Feld[x][y + 2] = EL_ROCK;
6840       TEST_DrawLevelField(x, y + 2);
6841
6842       object_hit = TRUE;
6843     }
6844 #endif
6845
6846     if (object_hit)
6847       smashed = MovingOrBlocked2Element(x, y + 1);
6848
6849     impact = (last_line || object_hit);
6850   }
6851
6852   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6853   {
6854     SplashAcid(x, y + 1);
6855     return;
6856   }
6857
6858   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6859   /* only reset graphic animation if graphic really changes after impact */
6860   if (impact &&
6861       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6862   {
6863     ResetGfxAnimation(x, y);
6864     TEST_DrawLevelField(x, y);
6865   }
6866
6867   if (impact && CAN_EXPLODE_IMPACT(element))
6868   {
6869     Bang(x, y);
6870     return;
6871   }
6872   else if (impact && element == EL_PEARL &&
6873            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6874   {
6875     ResetGfxAnimation(x, y);
6876
6877     Feld[x][y] = EL_PEARL_BREAKING;
6878     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6879     return;
6880   }
6881   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6882   {
6883     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6884
6885     return;
6886   }
6887
6888   if (impact && element == EL_AMOEBA_DROP)
6889   {
6890     if (object_hit && IS_PLAYER(x, y + 1))
6891       KillPlayerUnlessEnemyProtected(x, y + 1);
6892     else if (object_hit && smashed == EL_PENGUIN)
6893       Bang(x, y + 1);
6894     else
6895     {
6896       Feld[x][y] = EL_AMOEBA_GROWING;
6897       Store[x][y] = EL_AMOEBA_WET;
6898
6899       ResetRandomAnimationValue(x, y);
6900     }
6901     return;
6902   }
6903
6904   if (object_hit)               /* check which object was hit */
6905   {
6906     if ((CAN_PASS_MAGIC_WALL(element) && 
6907          (smashed == EL_MAGIC_WALL ||
6908           smashed == EL_BD_MAGIC_WALL)) ||
6909         (CAN_PASS_DC_MAGIC_WALL(element) &&
6910          smashed == EL_DC_MAGIC_WALL))
6911     {
6912       int xx, yy;
6913       int activated_magic_wall =
6914         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6915          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6916          EL_DC_MAGIC_WALL_ACTIVE);
6917
6918       /* activate magic wall / mill */
6919       SCAN_PLAYFIELD(xx, yy)
6920       {
6921         if (Feld[xx][yy] == smashed)
6922           Feld[xx][yy] = activated_magic_wall;
6923       }
6924
6925       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6926       game.magic_wall_active = TRUE;
6927
6928       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6929                             SND_MAGIC_WALL_ACTIVATING :
6930                             smashed == EL_BD_MAGIC_WALL ?
6931                             SND_BD_MAGIC_WALL_ACTIVATING :
6932                             SND_DC_MAGIC_WALL_ACTIVATING));
6933     }
6934
6935     if (IS_PLAYER(x, y + 1))
6936     {
6937       if (CAN_SMASH_PLAYER(element))
6938       {
6939         KillPlayerUnlessEnemyProtected(x, y + 1);
6940         return;
6941       }
6942     }
6943     else if (smashed == EL_PENGUIN)
6944     {
6945       if (CAN_SMASH_PLAYER(element))
6946       {
6947         Bang(x, y + 1);
6948         return;
6949       }
6950     }
6951     else if (element == EL_BD_DIAMOND)
6952     {
6953       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6954       {
6955         Bang(x, y + 1);
6956         return;
6957       }
6958     }
6959     else if (((element == EL_SP_INFOTRON ||
6960                element == EL_SP_ZONK) &&
6961               (smashed == EL_SP_SNIKSNAK ||
6962                smashed == EL_SP_ELECTRON ||
6963                smashed == EL_SP_DISK_ORANGE)) ||
6964              (element == EL_SP_INFOTRON &&
6965               smashed == EL_SP_DISK_YELLOW))
6966     {
6967       Bang(x, y + 1);
6968       return;
6969     }
6970     else if (CAN_SMASH_EVERYTHING(element))
6971     {
6972       if (IS_CLASSIC_ENEMY(smashed) ||
6973           CAN_EXPLODE_SMASHED(smashed))
6974       {
6975         Bang(x, y + 1);
6976         return;
6977       }
6978       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6979       {
6980         if (smashed == EL_LAMP ||
6981             smashed == EL_LAMP_ACTIVE)
6982         {
6983           Bang(x, y + 1);
6984           return;
6985         }
6986         else if (smashed == EL_NUT)
6987         {
6988           Feld[x][y + 1] = EL_NUT_BREAKING;
6989           PlayLevelSound(x, y, SND_NUT_BREAKING);
6990           RaiseScoreElement(EL_NUT);
6991           return;
6992         }
6993         else if (smashed == EL_PEARL)
6994         {
6995           ResetGfxAnimation(x, y);
6996
6997           Feld[x][y + 1] = EL_PEARL_BREAKING;
6998           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6999           return;
7000         }
7001         else if (smashed == EL_DIAMOND)
7002         {
7003           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7004           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7005           return;
7006         }
7007         else if (IS_BELT_SWITCH(smashed))
7008         {
7009           ToggleBeltSwitch(x, y + 1);
7010         }
7011         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7012                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7013                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7014                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7015         {
7016           ToggleSwitchgateSwitch(x, y + 1);
7017         }
7018         else if (smashed == EL_LIGHT_SWITCH ||
7019                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7020         {
7021           ToggleLightSwitch(x, y + 1);
7022         }
7023         else
7024         {
7025 #if 0
7026           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7027 #endif
7028
7029           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7030
7031           CheckElementChangeBySide(x, y + 1, smashed, element,
7032                                    CE_SWITCHED, CH_SIDE_TOP);
7033           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7034                                             CH_SIDE_TOP);
7035         }
7036       }
7037       else
7038       {
7039         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7040       }
7041     }
7042   }
7043
7044   /* play sound of magic wall / mill */
7045   if (!last_line &&
7046       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7047        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7048        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7049   {
7050     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7051       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7052     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7053       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7054     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7055       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7056
7057     return;
7058   }
7059
7060   /* play sound of object that hits the ground */
7061   if (last_line || object_hit)
7062     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7063 }
7064
7065 inline static void TurnRoundExt(int x, int y)
7066 {
7067   static struct
7068   {
7069     int dx, dy;
7070   } move_xy[] =
7071   {
7072     {  0,  0 },
7073     { -1,  0 },
7074     { +1,  0 },
7075     {  0,  0 },
7076     {  0, -1 },
7077     {  0,  0 }, { 0, 0 }, { 0, 0 },
7078     {  0, +1 }
7079   };
7080   static struct
7081   {
7082     int left, right, back;
7083   } turn[] =
7084   {
7085     { 0,        0,              0        },
7086     { MV_DOWN,  MV_UP,          MV_RIGHT },
7087     { MV_UP,    MV_DOWN,        MV_LEFT  },
7088     { 0,        0,              0        },
7089     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7090     { 0,        0,              0        },
7091     { 0,        0,              0        },
7092     { 0,        0,              0        },
7093     { MV_RIGHT, MV_LEFT,        MV_UP    }
7094   };
7095
7096   int element = Feld[x][y];
7097   int move_pattern = element_info[element].move_pattern;
7098
7099   int old_move_dir = MovDir[x][y];
7100   int left_dir  = turn[old_move_dir].left;
7101   int right_dir = turn[old_move_dir].right;
7102   int back_dir  = turn[old_move_dir].back;
7103
7104   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7105   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7106   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7107   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7108
7109   int left_x  = x + left_dx,  left_y  = y + left_dy;
7110   int right_x = x + right_dx, right_y = y + right_dy;
7111   int move_x  = x + move_dx,  move_y  = y + move_dy;
7112
7113   int xx, yy;
7114
7115   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7116   {
7117     TestIfBadThingTouchesOtherBadThing(x, y);
7118
7119     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7120       MovDir[x][y] = right_dir;
7121     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7122       MovDir[x][y] = left_dir;
7123
7124     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7125       MovDelay[x][y] = 9;
7126     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7127       MovDelay[x][y] = 1;
7128   }
7129   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7130   {
7131     TestIfBadThingTouchesOtherBadThing(x, y);
7132
7133     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7134       MovDir[x][y] = left_dir;
7135     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7136       MovDir[x][y] = right_dir;
7137
7138     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7139       MovDelay[x][y] = 9;
7140     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7141       MovDelay[x][y] = 1;
7142   }
7143   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7144   {
7145     TestIfBadThingTouchesOtherBadThing(x, y);
7146
7147     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7148       MovDir[x][y] = left_dir;
7149     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7150       MovDir[x][y] = right_dir;
7151
7152     if (MovDir[x][y] != old_move_dir)
7153       MovDelay[x][y] = 9;
7154   }
7155   else if (element == EL_YAMYAM)
7156   {
7157     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7158     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7159
7160     if (can_turn_left && can_turn_right)
7161       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7162     else if (can_turn_left)
7163       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7164     else if (can_turn_right)
7165       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7166     else
7167       MovDir[x][y] = back_dir;
7168
7169     MovDelay[x][y] = 16 + 16 * RND(3);
7170   }
7171   else if (element == EL_DARK_YAMYAM)
7172   {
7173     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7174                                                          left_x, left_y);
7175     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7176                                                          right_x, right_y);
7177
7178     if (can_turn_left && can_turn_right)
7179       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7180     else if (can_turn_left)
7181       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7182     else if (can_turn_right)
7183       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7184     else
7185       MovDir[x][y] = back_dir;
7186
7187     MovDelay[x][y] = 16 + 16 * RND(3);
7188   }
7189   else if (element == EL_PACMAN)
7190   {
7191     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7192     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7193
7194     if (can_turn_left && can_turn_right)
7195       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7196     else if (can_turn_left)
7197       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7198     else if (can_turn_right)
7199       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7200     else
7201       MovDir[x][y] = back_dir;
7202
7203     MovDelay[x][y] = 6 + RND(40);
7204   }
7205   else if (element == EL_PIG)
7206   {
7207     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7208     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7209     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7210     boolean should_turn_left, should_turn_right, should_move_on;
7211     int rnd_value = 24;
7212     int rnd = RND(rnd_value);
7213
7214     should_turn_left = (can_turn_left &&
7215                         (!can_move_on ||
7216                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7217                                                    y + back_dy + left_dy)));
7218     should_turn_right = (can_turn_right &&
7219                          (!can_move_on ||
7220                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7221                                                     y + back_dy + right_dy)));
7222     should_move_on = (can_move_on &&
7223                       (!can_turn_left ||
7224                        !can_turn_right ||
7225                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7226                                                  y + move_dy + left_dy) ||
7227                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7228                                                  y + move_dy + right_dy)));
7229
7230     if (should_turn_left || should_turn_right || should_move_on)
7231     {
7232       if (should_turn_left && should_turn_right && should_move_on)
7233         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7234                         rnd < 2 * rnd_value / 3 ? right_dir :
7235                         old_move_dir);
7236       else if (should_turn_left && should_turn_right)
7237         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7238       else if (should_turn_left && should_move_on)
7239         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7240       else if (should_turn_right && should_move_on)
7241         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7242       else if (should_turn_left)
7243         MovDir[x][y] = left_dir;
7244       else if (should_turn_right)
7245         MovDir[x][y] = right_dir;
7246       else if (should_move_on)
7247         MovDir[x][y] = old_move_dir;
7248     }
7249     else if (can_move_on && rnd > rnd_value / 8)
7250       MovDir[x][y] = old_move_dir;
7251     else if (can_turn_left && can_turn_right)
7252       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7253     else if (can_turn_left && rnd > rnd_value / 8)
7254       MovDir[x][y] = left_dir;
7255     else if (can_turn_right && rnd > rnd_value/8)
7256       MovDir[x][y] = right_dir;
7257     else
7258       MovDir[x][y] = back_dir;
7259
7260     xx = x + move_xy[MovDir[x][y]].dx;
7261     yy = y + move_xy[MovDir[x][y]].dy;
7262
7263     if (!IN_LEV_FIELD(xx, yy) ||
7264         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7265       MovDir[x][y] = old_move_dir;
7266
7267     MovDelay[x][y] = 0;
7268   }
7269   else if (element == EL_DRAGON)
7270   {
7271     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7272     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7273     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7274     int rnd_value = 24;
7275     int rnd = RND(rnd_value);
7276
7277     if (can_move_on && rnd > rnd_value / 8)
7278       MovDir[x][y] = old_move_dir;
7279     else if (can_turn_left && can_turn_right)
7280       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7281     else if (can_turn_left && rnd > rnd_value / 8)
7282       MovDir[x][y] = left_dir;
7283     else if (can_turn_right && rnd > rnd_value / 8)
7284       MovDir[x][y] = right_dir;
7285     else
7286       MovDir[x][y] = back_dir;
7287
7288     xx = x + move_xy[MovDir[x][y]].dx;
7289     yy = y + move_xy[MovDir[x][y]].dy;
7290
7291     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7292       MovDir[x][y] = old_move_dir;
7293
7294     MovDelay[x][y] = 0;
7295   }
7296   else if (element == EL_MOLE)
7297   {
7298     boolean can_move_on =
7299       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7300                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7301                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7302     if (!can_move_on)
7303     {
7304       boolean can_turn_left =
7305         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7306                               IS_AMOEBOID(Feld[left_x][left_y])));
7307
7308       boolean can_turn_right =
7309         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7310                               IS_AMOEBOID(Feld[right_x][right_y])));
7311
7312       if (can_turn_left && can_turn_right)
7313         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7314       else if (can_turn_left)
7315         MovDir[x][y] = left_dir;
7316       else
7317         MovDir[x][y] = right_dir;
7318     }
7319
7320     if (MovDir[x][y] != old_move_dir)
7321       MovDelay[x][y] = 9;
7322   }
7323   else if (element == EL_BALLOON)
7324   {
7325     MovDir[x][y] = game.wind_direction;
7326     MovDelay[x][y] = 0;
7327   }
7328   else if (element == EL_SPRING)
7329   {
7330 #if USE_NEW_SPRING_BUMPER
7331     if (MovDir[x][y] & MV_HORIZONTAL)
7332     {
7333       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7334           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7335       {
7336         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7337         ResetGfxAnimation(move_x, move_y);
7338         TEST_DrawLevelField(move_x, move_y);
7339
7340         MovDir[x][y] = back_dir;
7341       }
7342       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7343                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7344         MovDir[x][y] = MV_NONE;
7345     }
7346 #else
7347     if (MovDir[x][y] & MV_HORIZONTAL &&
7348         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7349          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7350       MovDir[x][y] = MV_NONE;
7351 #endif
7352
7353     MovDelay[x][y] = 0;
7354   }
7355   else if (element == EL_ROBOT ||
7356            element == EL_SATELLITE ||
7357            element == EL_PENGUIN ||
7358            element == EL_EMC_ANDROID)
7359   {
7360     int attr_x = -1, attr_y = -1;
7361
7362     if (AllPlayersGone)
7363     {
7364       attr_x = ExitX;
7365       attr_y = ExitY;
7366     }
7367     else
7368     {
7369       int i;
7370
7371       for (i = 0; i < MAX_PLAYERS; i++)
7372       {
7373         struct PlayerInfo *player = &stored_player[i];
7374         int jx = player->jx, jy = player->jy;
7375
7376         if (!player->active)
7377           continue;
7378
7379         if (attr_x == -1 ||
7380             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7381         {
7382           attr_x = jx;
7383           attr_y = jy;
7384         }
7385       }
7386     }
7387
7388     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7389         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7390          game.engine_version < VERSION_IDENT(3,1,0,0)))
7391     {
7392       attr_x = ZX;
7393       attr_y = ZY;
7394     }
7395
7396     if (element == EL_PENGUIN)
7397     {
7398       int i;
7399       static int xy[4][2] =
7400       {
7401         { 0, -1 },
7402         { -1, 0 },
7403         { +1, 0 },
7404         { 0, +1 }
7405       };
7406
7407       for (i = 0; i < NUM_DIRECTIONS; i++)
7408       {
7409         int ex = x + xy[i][0];
7410         int ey = y + xy[i][1];
7411
7412         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7413                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7414                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7415                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7416         {
7417           attr_x = ex;
7418           attr_y = ey;
7419           break;
7420         }
7421       }
7422     }
7423
7424     MovDir[x][y] = MV_NONE;
7425     if (attr_x < x)
7426       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7427     else if (attr_x > x)
7428       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7429     if (attr_y < y)
7430       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7431     else if (attr_y > y)
7432       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7433
7434     if (element == EL_ROBOT)
7435     {
7436       int newx, newy;
7437
7438       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7439         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7440       Moving2Blocked(x, y, &newx, &newy);
7441
7442       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7443         MovDelay[x][y] = 8 + 8 * !RND(3);
7444       else
7445         MovDelay[x][y] = 16;
7446     }
7447     else if (element == EL_PENGUIN)
7448     {
7449       int newx, newy;
7450
7451       MovDelay[x][y] = 1;
7452
7453       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7454       {
7455         boolean first_horiz = RND(2);
7456         int new_move_dir = MovDir[x][y];
7457
7458         MovDir[x][y] =
7459           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7460         Moving2Blocked(x, y, &newx, &newy);
7461
7462         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7463           return;
7464
7465         MovDir[x][y] =
7466           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7467         Moving2Blocked(x, y, &newx, &newy);
7468
7469         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7470           return;
7471
7472         MovDir[x][y] = old_move_dir;
7473         return;
7474       }
7475     }
7476     else if (element == EL_SATELLITE)
7477     {
7478       int newx, newy;
7479
7480       MovDelay[x][y] = 1;
7481
7482       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7483       {
7484         boolean first_horiz = RND(2);
7485         int new_move_dir = MovDir[x][y];
7486
7487         MovDir[x][y] =
7488           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489         Moving2Blocked(x, y, &newx, &newy);
7490
7491         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7492           return;
7493
7494         MovDir[x][y] =
7495           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496         Moving2Blocked(x, y, &newx, &newy);
7497
7498         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7499           return;
7500
7501         MovDir[x][y] = old_move_dir;
7502         return;
7503       }
7504     }
7505     else if (element == EL_EMC_ANDROID)
7506     {
7507       static int check_pos[16] =
7508       {
7509         -1,             /*  0 => (invalid)          */
7510         7,              /*  1 => MV_LEFT            */
7511         3,              /*  2 => MV_RIGHT           */
7512         -1,             /*  3 => (invalid)          */
7513         1,              /*  4 =>            MV_UP   */
7514         0,              /*  5 => MV_LEFT  | MV_UP   */
7515         2,              /*  6 => MV_RIGHT | MV_UP   */
7516         -1,             /*  7 => (invalid)          */
7517         5,              /*  8 =>            MV_DOWN */
7518         6,              /*  9 => MV_LEFT  | MV_DOWN */
7519         4,              /* 10 => MV_RIGHT | MV_DOWN */
7520         -1,             /* 11 => (invalid)          */
7521         -1,             /* 12 => (invalid)          */
7522         -1,             /* 13 => (invalid)          */
7523         -1,             /* 14 => (invalid)          */
7524         -1,             /* 15 => (invalid)          */
7525       };
7526       static struct
7527       {
7528         int dx, dy;
7529         int dir;
7530       } check_xy[8] =
7531       {
7532         { -1, -1,       MV_LEFT  | MV_UP   },
7533         {  0, -1,                  MV_UP   },
7534         { +1, -1,       MV_RIGHT | MV_UP   },
7535         { +1,  0,       MV_RIGHT           },
7536         { +1, +1,       MV_RIGHT | MV_DOWN },
7537         {  0, +1,                  MV_DOWN },
7538         { -1, +1,       MV_LEFT  | MV_DOWN },
7539         { -1,  0,       MV_LEFT            },
7540       };
7541       int start_pos, check_order;
7542       boolean can_clone = FALSE;
7543       int i;
7544
7545       /* check if there is any free field around current position */
7546       for (i = 0; i < 8; i++)
7547       {
7548         int newx = x + check_xy[i].dx;
7549         int newy = y + check_xy[i].dy;
7550
7551         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7552         {
7553           can_clone = TRUE;
7554
7555           break;
7556         }
7557       }
7558
7559       if (can_clone)            /* randomly find an element to clone */
7560       {
7561         can_clone = FALSE;
7562
7563         start_pos = check_pos[RND(8)];
7564         check_order = (RND(2) ? -1 : +1);
7565
7566         for (i = 0; i < 8; i++)
7567         {
7568           int pos_raw = start_pos + i * check_order;
7569           int pos = (pos_raw + 8) % 8;
7570           int newx = x + check_xy[pos].dx;
7571           int newy = y + check_xy[pos].dy;
7572
7573           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7574           {
7575             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7576             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7577
7578             Store[x][y] = Feld[newx][newy];
7579
7580             can_clone = TRUE;
7581
7582             break;
7583           }
7584         }
7585       }
7586
7587       if (can_clone)            /* randomly find a direction to move */
7588       {
7589         can_clone = FALSE;
7590
7591         start_pos = check_pos[RND(8)];
7592         check_order = (RND(2) ? -1 : +1);
7593
7594         for (i = 0; i < 8; i++)
7595         {
7596           int pos_raw = start_pos + i * check_order;
7597           int pos = (pos_raw + 8) % 8;
7598           int newx = x + check_xy[pos].dx;
7599           int newy = y + check_xy[pos].dy;
7600           int new_move_dir = check_xy[pos].dir;
7601
7602           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7603           {
7604             MovDir[x][y] = new_move_dir;
7605             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7606
7607             can_clone = TRUE;
7608
7609             break;
7610           }
7611         }
7612       }
7613
7614       if (can_clone)            /* cloning and moving successful */
7615         return;
7616
7617       /* cannot clone -- try to move towards player */
7618
7619       start_pos = check_pos[MovDir[x][y] & 0x0f];
7620       check_order = (RND(2) ? -1 : +1);
7621
7622       for (i = 0; i < 3; i++)
7623       {
7624         /* first check start_pos, then previous/next or (next/previous) pos */
7625         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7626         int pos = (pos_raw + 8) % 8;
7627         int newx = x + check_xy[pos].dx;
7628         int newy = y + check_xy[pos].dy;
7629         int new_move_dir = check_xy[pos].dir;
7630
7631         if (IS_PLAYER(newx, newy))
7632           break;
7633
7634         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7635         {
7636           MovDir[x][y] = new_move_dir;
7637           MovDelay[x][y] = level.android_move_time * 8 + 1;
7638
7639           break;
7640         }
7641       }
7642     }
7643   }
7644   else if (move_pattern == MV_TURNING_LEFT ||
7645            move_pattern == MV_TURNING_RIGHT ||
7646            move_pattern == MV_TURNING_LEFT_RIGHT ||
7647            move_pattern == MV_TURNING_RIGHT_LEFT ||
7648            move_pattern == MV_TURNING_RANDOM ||
7649            move_pattern == MV_ALL_DIRECTIONS)
7650   {
7651     boolean can_turn_left =
7652       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7653     boolean can_turn_right =
7654       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7655
7656     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7657       return;
7658
7659     if (move_pattern == MV_TURNING_LEFT)
7660       MovDir[x][y] = left_dir;
7661     else if (move_pattern == MV_TURNING_RIGHT)
7662       MovDir[x][y] = right_dir;
7663     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7664       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7665     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7666       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7667     else if (move_pattern == MV_TURNING_RANDOM)
7668       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7669                       can_turn_right && !can_turn_left ? right_dir :
7670                       RND(2) ? left_dir : right_dir);
7671     else if (can_turn_left && can_turn_right)
7672       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7673     else if (can_turn_left)
7674       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7675     else if (can_turn_right)
7676       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7677     else
7678       MovDir[x][y] = back_dir;
7679
7680     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7681   }
7682   else if (move_pattern == MV_HORIZONTAL ||
7683            move_pattern == MV_VERTICAL)
7684   {
7685     if (move_pattern & old_move_dir)
7686       MovDir[x][y] = back_dir;
7687     else if (move_pattern == MV_HORIZONTAL)
7688       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7689     else if (move_pattern == MV_VERTICAL)
7690       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7691
7692     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7693   }
7694   else if (move_pattern & MV_ANY_DIRECTION)
7695   {
7696     MovDir[x][y] = move_pattern;
7697     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7698   }
7699   else if (move_pattern & MV_WIND_DIRECTION)
7700   {
7701     MovDir[x][y] = game.wind_direction;
7702     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7703   }
7704   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7705   {
7706     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7707       MovDir[x][y] = left_dir;
7708     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7709       MovDir[x][y] = right_dir;
7710
7711     if (MovDir[x][y] != old_move_dir)
7712       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7713   }
7714   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7715   {
7716     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7717       MovDir[x][y] = right_dir;
7718     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7719       MovDir[x][y] = left_dir;
7720
7721     if (MovDir[x][y] != old_move_dir)
7722       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7723   }
7724   else if (move_pattern == MV_TOWARDS_PLAYER ||
7725            move_pattern == MV_AWAY_FROM_PLAYER)
7726   {
7727     int attr_x = -1, attr_y = -1;
7728     int newx, newy;
7729     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7730
7731     if (AllPlayersGone)
7732     {
7733       attr_x = ExitX;
7734       attr_y = ExitY;
7735     }
7736     else
7737     {
7738       int i;
7739
7740       for (i = 0; i < MAX_PLAYERS; i++)
7741       {
7742         struct PlayerInfo *player = &stored_player[i];
7743         int jx = player->jx, jy = player->jy;
7744
7745         if (!player->active)
7746           continue;
7747
7748         if (attr_x == -1 ||
7749             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7750         {
7751           attr_x = jx;
7752           attr_y = jy;
7753         }
7754       }
7755     }
7756
7757     MovDir[x][y] = MV_NONE;
7758     if (attr_x < x)
7759       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7760     else if (attr_x > x)
7761       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7762     if (attr_y < y)
7763       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7764     else if (attr_y > y)
7765       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7766
7767     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768
7769     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7770     {
7771       boolean first_horiz = RND(2);
7772       int new_move_dir = MovDir[x][y];
7773
7774       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7775       {
7776         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7777         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7778
7779         return;
7780       }
7781
7782       MovDir[x][y] =
7783         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7784       Moving2Blocked(x, y, &newx, &newy);
7785
7786       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7787         return;
7788
7789       MovDir[x][y] =
7790         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7791       Moving2Blocked(x, y, &newx, &newy);
7792
7793       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7794         return;
7795
7796       MovDir[x][y] = old_move_dir;
7797     }
7798   }
7799   else if (move_pattern == MV_WHEN_PUSHED ||
7800            move_pattern == MV_WHEN_DROPPED)
7801   {
7802     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7803       MovDir[x][y] = MV_NONE;
7804
7805     MovDelay[x][y] = 0;
7806   }
7807   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7808   {
7809     static int test_xy[7][2] =
7810     {
7811       { 0, -1 },
7812       { -1, 0 },
7813       { +1, 0 },
7814       { 0, +1 },
7815       { 0, -1 },
7816       { -1, 0 },
7817       { +1, 0 },
7818     };
7819     static int test_dir[7] =
7820     {
7821       MV_UP,
7822       MV_LEFT,
7823       MV_RIGHT,
7824       MV_DOWN,
7825       MV_UP,
7826       MV_LEFT,
7827       MV_RIGHT,
7828     };
7829     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7830     int move_preference = -1000000;     /* start with very low preference */
7831     int new_move_dir = MV_NONE;
7832     int start_test = RND(4);
7833     int i;
7834
7835     for (i = 0; i < NUM_DIRECTIONS; i++)
7836     {
7837       int move_dir = test_dir[start_test + i];
7838       int move_dir_preference;
7839
7840       xx = x + test_xy[start_test + i][0];
7841       yy = y + test_xy[start_test + i][1];
7842
7843       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7844           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7845       {
7846         new_move_dir = move_dir;
7847
7848         break;
7849       }
7850
7851       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7852         continue;
7853
7854       move_dir_preference = -1 * RunnerVisit[xx][yy];
7855       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7856         move_dir_preference = PlayerVisit[xx][yy];
7857
7858       if (move_dir_preference > move_preference)
7859       {
7860         /* prefer field that has not been visited for the longest time */
7861         move_preference = move_dir_preference;
7862         new_move_dir = move_dir;
7863       }
7864       else if (move_dir_preference == move_preference &&
7865                move_dir == old_move_dir)
7866       {
7867         /* prefer last direction when all directions are preferred equally */
7868         move_preference = move_dir_preference;
7869         new_move_dir = move_dir;
7870       }
7871     }
7872
7873     MovDir[x][y] = new_move_dir;
7874     if (old_move_dir != new_move_dir)
7875       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7876   }
7877 }
7878
7879 static void TurnRound(int x, int y)
7880 {
7881   int direction = MovDir[x][y];
7882
7883   TurnRoundExt(x, y);
7884
7885   GfxDir[x][y] = MovDir[x][y];
7886
7887   if (direction != MovDir[x][y])
7888     GfxFrame[x][y] = 0;
7889
7890   if (MovDelay[x][y])
7891     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7892
7893   ResetGfxFrame(x, y, FALSE);
7894 }
7895
7896 static boolean JustBeingPushed(int x, int y)
7897 {
7898   int i;
7899
7900   for (i = 0; i < MAX_PLAYERS; i++)
7901   {
7902     struct PlayerInfo *player = &stored_player[i];
7903
7904     if (player->active && player->is_pushing && player->MovPos)
7905     {
7906       int next_jx = player->jx + (player->jx - player->last_jx);
7907       int next_jy = player->jy + (player->jy - player->last_jy);
7908
7909       if (x == next_jx && y == next_jy)
7910         return TRUE;
7911     }
7912   }
7913
7914   return FALSE;
7915 }
7916
7917 void StartMoving(int x, int y)
7918 {
7919   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7920   int element = Feld[x][y];
7921
7922   if (Stop[x][y])
7923     return;
7924
7925   if (MovDelay[x][y] == 0)
7926     GfxAction[x][y] = ACTION_DEFAULT;
7927
7928   if (CAN_FALL(element) && y < lev_fieldy - 1)
7929   {
7930     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7931         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7932       if (JustBeingPushed(x, y))
7933         return;
7934
7935     if (element == EL_QUICKSAND_FULL)
7936     {
7937       if (IS_FREE(x, y + 1))
7938       {
7939         InitMovingField(x, y, MV_DOWN);
7940         started_moving = TRUE;
7941
7942         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7943 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7944         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7945           Store[x][y] = EL_ROCK;
7946 #else
7947         Store[x][y] = EL_ROCK;
7948 #endif
7949
7950         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7951       }
7952       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7953       {
7954         if (!MovDelay[x][y])
7955         {
7956           MovDelay[x][y] = TILEY + 1;
7957
7958           ResetGfxAnimation(x, y);
7959           ResetGfxAnimation(x, y + 1);
7960         }
7961
7962         if (MovDelay[x][y])
7963         {
7964           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7965           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7966
7967           MovDelay[x][y]--;
7968           if (MovDelay[x][y])
7969             return;
7970         }
7971
7972         Feld[x][y] = EL_QUICKSAND_EMPTY;
7973         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7974         Store[x][y + 1] = Store[x][y];
7975         Store[x][y] = 0;
7976
7977         PlayLevelSoundAction(x, y, ACTION_FILLING);
7978       }
7979       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7980       {
7981         if (!MovDelay[x][y])
7982         {
7983           MovDelay[x][y] = TILEY + 1;
7984
7985           ResetGfxAnimation(x, y);
7986           ResetGfxAnimation(x, y + 1);
7987         }
7988
7989         if (MovDelay[x][y])
7990         {
7991           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7992           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7993
7994           MovDelay[x][y]--;
7995           if (MovDelay[x][y])
7996             return;
7997         }
7998
7999         Feld[x][y] = EL_QUICKSAND_EMPTY;
8000         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8001         Store[x][y + 1] = Store[x][y];
8002         Store[x][y] = 0;
8003
8004         PlayLevelSoundAction(x, y, ACTION_FILLING);
8005       }
8006     }
8007     else if (element == EL_QUICKSAND_FAST_FULL)
8008     {
8009       if (IS_FREE(x, y + 1))
8010       {
8011         InitMovingField(x, y, MV_DOWN);
8012         started_moving = TRUE;
8013
8014         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8015 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8016         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8017           Store[x][y] = EL_ROCK;
8018 #else
8019         Store[x][y] = EL_ROCK;
8020 #endif
8021
8022         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8023       }
8024       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8025       {
8026         if (!MovDelay[x][y])
8027         {
8028           MovDelay[x][y] = TILEY + 1;
8029
8030           ResetGfxAnimation(x, y);
8031           ResetGfxAnimation(x, y + 1);
8032         }
8033
8034         if (MovDelay[x][y])
8035         {
8036           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8037           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8038
8039           MovDelay[x][y]--;
8040           if (MovDelay[x][y])
8041             return;
8042         }
8043
8044         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8045         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8046         Store[x][y + 1] = Store[x][y];
8047         Store[x][y] = 0;
8048
8049         PlayLevelSoundAction(x, y, ACTION_FILLING);
8050       }
8051       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8052       {
8053         if (!MovDelay[x][y])
8054         {
8055           MovDelay[x][y] = TILEY + 1;
8056
8057           ResetGfxAnimation(x, y);
8058           ResetGfxAnimation(x, y + 1);
8059         }
8060
8061         if (MovDelay[x][y])
8062         {
8063           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8064           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8065
8066           MovDelay[x][y]--;
8067           if (MovDelay[x][y])
8068             return;
8069         }
8070
8071         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8072         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8073         Store[x][y + 1] = Store[x][y];
8074         Store[x][y] = 0;
8075
8076         PlayLevelSoundAction(x, y, ACTION_FILLING);
8077       }
8078     }
8079     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8080              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8081     {
8082       InitMovingField(x, y, MV_DOWN);
8083       started_moving = TRUE;
8084
8085       Feld[x][y] = EL_QUICKSAND_FILLING;
8086       Store[x][y] = element;
8087
8088       PlayLevelSoundAction(x, y, ACTION_FILLING);
8089     }
8090     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8091              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8092     {
8093       InitMovingField(x, y, MV_DOWN);
8094       started_moving = TRUE;
8095
8096       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8097       Store[x][y] = element;
8098
8099       PlayLevelSoundAction(x, y, ACTION_FILLING);
8100     }
8101     else if (element == EL_MAGIC_WALL_FULL)
8102     {
8103       if (IS_FREE(x, y + 1))
8104       {
8105         InitMovingField(x, y, MV_DOWN);
8106         started_moving = TRUE;
8107
8108         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8109         Store[x][y] = EL_CHANGED(Store[x][y]);
8110       }
8111       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8112       {
8113         if (!MovDelay[x][y])
8114           MovDelay[x][y] = TILEY/4 + 1;
8115
8116         if (MovDelay[x][y])
8117         {
8118           MovDelay[x][y]--;
8119           if (MovDelay[x][y])
8120             return;
8121         }
8122
8123         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8124         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8125         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8126         Store[x][y] = 0;
8127       }
8128     }
8129     else if (element == EL_BD_MAGIC_WALL_FULL)
8130     {
8131       if (IS_FREE(x, y + 1))
8132       {
8133         InitMovingField(x, y, MV_DOWN);
8134         started_moving = TRUE;
8135
8136         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8137         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8138       }
8139       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8140       {
8141         if (!MovDelay[x][y])
8142           MovDelay[x][y] = TILEY/4 + 1;
8143
8144         if (MovDelay[x][y])
8145         {
8146           MovDelay[x][y]--;
8147           if (MovDelay[x][y])
8148             return;
8149         }
8150
8151         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8152         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8153         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8154         Store[x][y] = 0;
8155       }
8156     }
8157     else if (element == EL_DC_MAGIC_WALL_FULL)
8158     {
8159       if (IS_FREE(x, y + 1))
8160       {
8161         InitMovingField(x, y, MV_DOWN);
8162         started_moving = TRUE;
8163
8164         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8165         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8166       }
8167       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8168       {
8169         if (!MovDelay[x][y])
8170           MovDelay[x][y] = TILEY/4 + 1;
8171
8172         if (MovDelay[x][y])
8173         {
8174           MovDelay[x][y]--;
8175           if (MovDelay[x][y])
8176             return;
8177         }
8178
8179         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8180         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8181         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8182         Store[x][y] = 0;
8183       }
8184     }
8185     else if ((CAN_PASS_MAGIC_WALL(element) &&
8186               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8187                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8188              (CAN_PASS_DC_MAGIC_WALL(element) &&
8189               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8190
8191     {
8192       InitMovingField(x, y, MV_DOWN);
8193       started_moving = TRUE;
8194
8195       Feld[x][y] =
8196         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8197          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8198          EL_DC_MAGIC_WALL_FILLING);
8199       Store[x][y] = element;
8200     }
8201     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8202     {
8203       SplashAcid(x, y + 1);
8204
8205       InitMovingField(x, y, MV_DOWN);
8206       started_moving = TRUE;
8207
8208       Store[x][y] = EL_ACID;
8209     }
8210     else if (
8211 #if USE_FIX_IMPACT_COLLISION
8212              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8213               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8214 #else
8215              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8216               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8217 #endif
8218              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8219               CAN_FALL(element) && WasJustFalling[x][y] &&
8220               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8221
8222              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8223               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8224               (Feld[x][y + 1] == EL_BLOCKED)))
8225     {
8226       /* this is needed for a special case not covered by calling "Impact()"
8227          from "ContinueMoving()": if an element moves to a tile directly below
8228          another element which was just falling on that tile (which was empty
8229          in the previous frame), the falling element above would just stop
8230          instead of smashing the element below (in previous version, the above
8231          element was just checked for "moving" instead of "falling", resulting
8232          in incorrect smashes caused by horizontal movement of the above
8233          element; also, the case of the player being the element to smash was
8234          simply not covered here... :-/ ) */
8235
8236       CheckCollision[x][y] = 0;
8237       CheckImpact[x][y] = 0;
8238
8239       Impact(x, y);
8240     }
8241     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8242     {
8243       if (MovDir[x][y] == MV_NONE)
8244       {
8245         InitMovingField(x, y, MV_DOWN);
8246         started_moving = TRUE;
8247       }
8248     }
8249     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8250     {
8251       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8252         MovDir[x][y] = MV_DOWN;
8253
8254       InitMovingField(x, y, MV_DOWN);
8255       started_moving = TRUE;
8256     }
8257     else if (element == EL_AMOEBA_DROP)
8258     {
8259       Feld[x][y] = EL_AMOEBA_GROWING;
8260       Store[x][y] = EL_AMOEBA_WET;
8261     }
8262     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8263               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8264              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8265              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8266     {
8267       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8268                                 (IS_FREE(x - 1, y + 1) ||
8269                                  Feld[x - 1][y + 1] == EL_ACID));
8270       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8271                                 (IS_FREE(x + 1, y + 1) ||
8272                                  Feld[x + 1][y + 1] == EL_ACID));
8273       boolean can_fall_any  = (can_fall_left || can_fall_right);
8274       boolean can_fall_both = (can_fall_left && can_fall_right);
8275       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8276
8277 #if USE_NEW_ALL_SLIPPERY
8278       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8279       {
8280         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8281           can_fall_right = FALSE;
8282         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8283           can_fall_left = FALSE;
8284         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8285           can_fall_right = FALSE;
8286         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8287           can_fall_left = FALSE;
8288
8289         can_fall_any  = (can_fall_left || can_fall_right);
8290         can_fall_both = FALSE;
8291       }
8292 #else
8293       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8294       {
8295         if (slippery_type == SLIPPERY_ONLY_LEFT)
8296           can_fall_right = FALSE;
8297         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8298           can_fall_left = FALSE;
8299         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8300           can_fall_right = FALSE;
8301         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8302           can_fall_left = FALSE;
8303
8304         can_fall_any  = (can_fall_left || can_fall_right);
8305         can_fall_both = (can_fall_left && can_fall_right);
8306       }
8307 #endif
8308
8309 #if USE_NEW_ALL_SLIPPERY
8310 #else
8311 #if USE_NEW_SP_SLIPPERY
8312       /* !!! better use the same properties as for custom elements here !!! */
8313       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8314                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8315       {
8316         can_fall_right = FALSE;         /* slip down on left side */
8317         can_fall_both = FALSE;
8318       }
8319 #endif
8320 #endif
8321
8322 #if USE_NEW_ALL_SLIPPERY
8323       if (can_fall_both)
8324       {
8325         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8326           can_fall_right = FALSE;       /* slip down on left side */
8327         else
8328           can_fall_left = !(can_fall_right = RND(2));
8329
8330         can_fall_both = FALSE;
8331       }
8332 #else
8333       if (can_fall_both)
8334       {
8335         if (game.emulation == EMU_BOULDERDASH ||
8336             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8337           can_fall_right = FALSE;       /* slip down on left side */
8338         else
8339           can_fall_left = !(can_fall_right = RND(2));
8340
8341         can_fall_both = FALSE;
8342       }
8343 #endif
8344
8345       if (can_fall_any)
8346       {
8347         /* if not determined otherwise, prefer left side for slipping down */
8348         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8349         started_moving = TRUE;
8350       }
8351     }
8352 #if 0
8353     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8354 #else
8355     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8356 #endif
8357     {
8358       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8359       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8360       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8361       int belt_dir = game.belt_dir[belt_nr];
8362
8363       if ((belt_dir == MV_LEFT  && left_is_free) ||
8364           (belt_dir == MV_RIGHT && right_is_free))
8365       {
8366         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8367
8368         InitMovingField(x, y, belt_dir);
8369         started_moving = TRUE;
8370
8371         Pushed[x][y] = TRUE;
8372         Pushed[nextx][y] = TRUE;
8373
8374         GfxAction[x][y] = ACTION_DEFAULT;
8375       }
8376       else
8377       {
8378         MovDir[x][y] = 0;       /* if element was moving, stop it */
8379       }
8380     }
8381   }
8382
8383   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8384 #if 0
8385   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8386 #else
8387   if (CAN_MOVE(element) && !started_moving)
8388 #endif
8389   {
8390     int move_pattern = element_info[element].move_pattern;
8391     int newx, newy;
8392
8393 #if 0
8394 #if DEBUG
8395     if (MovDir[x][y] == MV_NONE)
8396     {
8397       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8398              x, y, element, element_info[element].token_name);
8399       printf("StartMoving(): This should never happen!\n");
8400     }
8401 #endif
8402 #endif
8403
8404     Moving2Blocked(x, y, &newx, &newy);
8405
8406     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8407       return;
8408
8409     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8410         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8411     {
8412       WasJustMoving[x][y] = 0;
8413       CheckCollision[x][y] = 0;
8414
8415       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8416
8417       if (Feld[x][y] != element)        /* element has changed */
8418         return;
8419     }
8420
8421     if (!MovDelay[x][y])        /* start new movement phase */
8422     {
8423       /* all objects that can change their move direction after each step
8424          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8425
8426       if (element != EL_YAMYAM &&
8427           element != EL_DARK_YAMYAM &&
8428           element != EL_PACMAN &&
8429           !(move_pattern & MV_ANY_DIRECTION) &&
8430           move_pattern != MV_TURNING_LEFT &&
8431           move_pattern != MV_TURNING_RIGHT &&
8432           move_pattern != MV_TURNING_LEFT_RIGHT &&
8433           move_pattern != MV_TURNING_RIGHT_LEFT &&
8434           move_pattern != MV_TURNING_RANDOM)
8435       {
8436         TurnRound(x, y);
8437
8438         if (MovDelay[x][y] && (element == EL_BUG ||
8439                                element == EL_SPACESHIP ||
8440                                element == EL_SP_SNIKSNAK ||
8441                                element == EL_SP_ELECTRON ||
8442                                element == EL_MOLE))
8443           TEST_DrawLevelField(x, y);
8444       }
8445     }
8446
8447     if (MovDelay[x][y])         /* wait some time before next movement */
8448     {
8449       MovDelay[x][y]--;
8450
8451       if (element == EL_ROBOT ||
8452           element == EL_YAMYAM ||
8453           element == EL_DARK_YAMYAM)
8454       {
8455         DrawLevelElementAnimationIfNeeded(x, y, element);
8456         PlayLevelSoundAction(x, y, ACTION_WAITING);
8457       }
8458       else if (element == EL_SP_ELECTRON)
8459         DrawLevelElementAnimationIfNeeded(x, y, element);
8460       else if (element == EL_DRAGON)
8461       {
8462         int i;
8463         int dir = MovDir[x][y];
8464         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8465         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8466         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8467                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8468                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8469                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8470         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8471
8472         GfxAction[x][y] = ACTION_ATTACKING;
8473
8474         if (IS_PLAYER(x, y))
8475           DrawPlayerField(x, y);
8476         else
8477           TEST_DrawLevelField(x, y);
8478
8479         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8480
8481         for (i = 1; i <= 3; i++)
8482         {
8483           int xx = x + i * dx;
8484           int yy = y + i * dy;
8485           int sx = SCREENX(xx);
8486           int sy = SCREENY(yy);
8487           int flame_graphic = graphic + (i - 1);
8488
8489           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8490             break;
8491
8492           if (MovDelay[x][y])
8493           {
8494             int flamed = MovingOrBlocked2Element(xx, yy);
8495
8496             /* !!! */
8497 #if 0
8498             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8499               Bang(xx, yy);
8500             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8501               RemoveMovingField(xx, yy);
8502             else
8503               RemoveField(xx, yy);
8504 #else
8505             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8506               Bang(xx, yy);
8507             else
8508               RemoveMovingField(xx, yy);
8509 #endif
8510
8511             ChangeDelay[xx][yy] = 0;
8512
8513             Feld[xx][yy] = EL_FLAMES;
8514
8515             if (IN_SCR_FIELD(sx, sy))
8516             {
8517               TEST_DrawLevelFieldCrumbled(xx, yy);
8518               DrawGraphic(sx, sy, flame_graphic, frame);
8519             }
8520           }
8521           else
8522           {
8523             if (Feld[xx][yy] == EL_FLAMES)
8524               Feld[xx][yy] = EL_EMPTY;
8525             TEST_DrawLevelField(xx, yy);
8526           }
8527         }
8528       }
8529
8530       if (MovDelay[x][y])       /* element still has to wait some time */
8531       {
8532         PlayLevelSoundAction(x, y, ACTION_WAITING);
8533
8534         return;
8535       }
8536     }
8537
8538     /* now make next step */
8539
8540     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8541
8542     if (DONT_COLLIDE_WITH(element) &&
8543         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8544         !PLAYER_ENEMY_PROTECTED(newx, newy))
8545     {
8546       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8547
8548       return;
8549     }
8550
8551     else if (CAN_MOVE_INTO_ACID(element) &&
8552              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8553              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8554              (MovDir[x][y] == MV_DOWN ||
8555               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8556     {
8557       SplashAcid(newx, newy);
8558       Store[x][y] = EL_ACID;
8559     }
8560     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8561     {
8562       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8563           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8564           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8565           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8566       {
8567         RemoveField(x, y);
8568         TEST_DrawLevelField(x, y);
8569
8570         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8571         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8572           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8573
8574         local_player->friends_still_needed--;
8575         if (!local_player->friends_still_needed &&
8576             !local_player->GameOver && AllPlayersGone)
8577           PlayerWins(local_player);
8578
8579         return;
8580       }
8581       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8582       {
8583         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8584           TEST_DrawLevelField(newx, newy);
8585         else
8586           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8587       }
8588       else if (!IS_FREE(newx, newy))
8589       {
8590         GfxAction[x][y] = ACTION_WAITING;
8591
8592         if (IS_PLAYER(x, y))
8593           DrawPlayerField(x, y);
8594         else
8595           TEST_DrawLevelField(x, y);
8596
8597         return;
8598       }
8599     }
8600     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8601     {
8602       if (IS_FOOD_PIG(Feld[newx][newy]))
8603       {
8604         if (IS_MOVING(newx, newy))
8605           RemoveMovingField(newx, newy);
8606         else
8607         {
8608           Feld[newx][newy] = EL_EMPTY;
8609           TEST_DrawLevelField(newx, newy);
8610         }
8611
8612         PlayLevelSound(x, y, SND_PIG_DIGGING);
8613       }
8614       else if (!IS_FREE(newx, newy))
8615       {
8616         if (IS_PLAYER(x, y))
8617           DrawPlayerField(x, y);
8618         else
8619           TEST_DrawLevelField(x, y);
8620
8621         return;
8622       }
8623     }
8624     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8625     {
8626       if (Store[x][y] != EL_EMPTY)
8627       {
8628         boolean can_clone = FALSE;
8629         int xx, yy;
8630
8631         /* check if element to clone is still there */
8632         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8633         {
8634           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8635           {
8636             can_clone = TRUE;
8637
8638             break;
8639           }
8640         }
8641
8642         /* cannot clone or target field not free anymore -- do not clone */
8643         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8644           Store[x][y] = EL_EMPTY;
8645       }
8646
8647       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8648       {
8649         if (IS_MV_DIAGONAL(MovDir[x][y]))
8650         {
8651           int diagonal_move_dir = MovDir[x][y];
8652           int stored = Store[x][y];
8653           int change_delay = 8;
8654           int graphic;
8655
8656           /* android is moving diagonally */
8657
8658           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8659
8660           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8661           GfxElement[x][y] = EL_EMC_ANDROID;
8662           GfxAction[x][y] = ACTION_SHRINKING;
8663           GfxDir[x][y] = diagonal_move_dir;
8664           ChangeDelay[x][y] = change_delay;
8665
8666           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8667                                    GfxDir[x][y]);
8668
8669           DrawLevelGraphicAnimation(x, y, graphic);
8670           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8671
8672           if (Feld[newx][newy] == EL_ACID)
8673           {
8674             SplashAcid(newx, newy);
8675
8676             return;
8677           }
8678
8679           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8680
8681           Store[newx][newy] = EL_EMC_ANDROID;
8682           GfxElement[newx][newy] = EL_EMC_ANDROID;
8683           GfxAction[newx][newy] = ACTION_GROWING;
8684           GfxDir[newx][newy] = diagonal_move_dir;
8685           ChangeDelay[newx][newy] = change_delay;
8686
8687           graphic = el_act_dir2img(GfxElement[newx][newy],
8688                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8689
8690           DrawLevelGraphicAnimation(newx, newy, graphic);
8691           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8692
8693           return;
8694         }
8695         else
8696         {
8697           Feld[newx][newy] = EL_EMPTY;
8698           TEST_DrawLevelField(newx, newy);
8699
8700           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8701         }
8702       }
8703       else if (!IS_FREE(newx, newy))
8704       {
8705 #if 0
8706         if (IS_PLAYER(x, y))
8707           DrawPlayerField(x, y);
8708         else
8709           TEST_DrawLevelField(x, y);
8710 #endif
8711
8712         return;
8713       }
8714     }
8715     else if (IS_CUSTOM_ELEMENT(element) &&
8716              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8717     {
8718 #if 1
8719       if (!DigFieldByCE(newx, newy, element))
8720         return;
8721 #else
8722       int new_element = Feld[newx][newy];
8723
8724       if (!IS_FREE(newx, newy))
8725       {
8726         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8727                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8728                       ACTION_BREAKING);
8729
8730         /* no element can dig solid indestructible elements */
8731         if (IS_INDESTRUCTIBLE(new_element) &&
8732             !IS_DIGGABLE(new_element) &&
8733             !IS_COLLECTIBLE(new_element))
8734           return;
8735
8736         if (AmoebaNr[newx][newy] &&
8737             (new_element == EL_AMOEBA_FULL ||
8738              new_element == EL_BD_AMOEBA ||
8739              new_element == EL_AMOEBA_GROWING))
8740         {
8741           AmoebaCnt[AmoebaNr[newx][newy]]--;
8742           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8743         }
8744
8745         if (IS_MOVING(newx, newy))
8746           RemoveMovingField(newx, newy);
8747         else
8748         {
8749           RemoveField(newx, newy);
8750           TEST_DrawLevelField(newx, newy);
8751         }
8752
8753         /* if digged element was about to explode, prevent the explosion */
8754         ExplodeField[newx][newy] = EX_TYPE_NONE;
8755
8756         PlayLevelSoundAction(x, y, action);
8757       }
8758
8759       Store[newx][newy] = EL_EMPTY;
8760
8761 #if 1
8762       /* this makes it possible to leave the removed element again */
8763       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8764         Store[newx][newy] = new_element;
8765 #else
8766       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8767       {
8768         int move_leave_element = element_info[element].move_leave_element;
8769
8770         /* this makes it possible to leave the removed element again */
8771         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8772                              new_element : move_leave_element);
8773       }
8774 #endif
8775
8776 #endif
8777
8778       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8779       {
8780         RunnerVisit[x][y] = FrameCounter;
8781         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8782       }
8783     }
8784     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8785     {
8786       if (!IS_FREE(newx, newy))
8787       {
8788         if (IS_PLAYER(x, y))
8789           DrawPlayerField(x, y);
8790         else
8791           TEST_DrawLevelField(x, y);
8792
8793         return;
8794       }
8795       else
8796       {
8797         boolean wanna_flame = !RND(10);
8798         int dx = newx - x, dy = newy - y;
8799         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8800         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8801         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8802                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8803         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8804                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8805
8806         if ((wanna_flame ||
8807              IS_CLASSIC_ENEMY(element1) ||
8808              IS_CLASSIC_ENEMY(element2)) &&
8809             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8810             element1 != EL_FLAMES && element2 != EL_FLAMES)
8811         {
8812           ResetGfxAnimation(x, y);
8813           GfxAction[x][y] = ACTION_ATTACKING;
8814
8815           if (IS_PLAYER(x, y))
8816             DrawPlayerField(x, y);
8817           else
8818             TEST_DrawLevelField(x, y);
8819
8820           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8821
8822           MovDelay[x][y] = 50;
8823
8824           /* !!! */
8825 #if 0
8826           RemoveField(newx, newy);
8827 #endif
8828           Feld[newx][newy] = EL_FLAMES;
8829           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8830           {
8831 #if 0
8832             RemoveField(newx1, newy1);
8833 #endif
8834             Feld[newx1][newy1] = EL_FLAMES;
8835           }
8836           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8837           {
8838 #if 0
8839             RemoveField(newx2, newy2);
8840 #endif
8841             Feld[newx2][newy2] = EL_FLAMES;
8842           }
8843
8844           return;
8845         }
8846       }
8847     }
8848     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8849              Feld[newx][newy] == EL_DIAMOND)
8850     {
8851       if (IS_MOVING(newx, newy))
8852         RemoveMovingField(newx, newy);
8853       else
8854       {
8855         Feld[newx][newy] = EL_EMPTY;
8856         TEST_DrawLevelField(newx, newy);
8857       }
8858
8859       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8860     }
8861     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8862              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8863     {
8864       if (AmoebaNr[newx][newy])
8865       {
8866         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8867         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8868             Feld[newx][newy] == EL_BD_AMOEBA)
8869           AmoebaCnt[AmoebaNr[newx][newy]]--;
8870       }
8871
8872 #if 0
8873       /* !!! test !!! */
8874       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8875       {
8876         RemoveMovingField(newx, newy);
8877       }
8878 #else
8879       if (IS_MOVING(newx, newy))
8880       {
8881         RemoveMovingField(newx, newy);
8882       }
8883 #endif
8884       else
8885       {
8886         Feld[newx][newy] = EL_EMPTY;
8887         TEST_DrawLevelField(newx, newy);
8888       }
8889
8890       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8891     }
8892     else if ((element == EL_PACMAN || element == EL_MOLE)
8893              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8894     {
8895       if (AmoebaNr[newx][newy])
8896       {
8897         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8898         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8899             Feld[newx][newy] == EL_BD_AMOEBA)
8900           AmoebaCnt[AmoebaNr[newx][newy]]--;
8901       }
8902
8903       if (element == EL_MOLE)
8904       {
8905         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8906         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8907
8908         ResetGfxAnimation(x, y);
8909         GfxAction[x][y] = ACTION_DIGGING;
8910         TEST_DrawLevelField(x, y);
8911
8912         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8913
8914         return;                         /* wait for shrinking amoeba */
8915       }
8916       else      /* element == EL_PACMAN */
8917       {
8918         Feld[newx][newy] = EL_EMPTY;
8919         TEST_DrawLevelField(newx, newy);
8920         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8921       }
8922     }
8923     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8924              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8925               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8926     {
8927       /* wait for shrinking amoeba to completely disappear */
8928       return;
8929     }
8930     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8931     {
8932       /* object was running against a wall */
8933
8934       TurnRound(x, y);
8935
8936 #if 0
8937       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8938       if (move_pattern & MV_ANY_DIRECTION &&
8939           move_pattern == MovDir[x][y])
8940       {
8941         int blocking_element =
8942           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8943
8944         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8945                                  MovDir[x][y]);
8946
8947         element = Feld[x][y];   /* element might have changed */
8948       }
8949 #endif
8950
8951       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8952         DrawLevelElementAnimation(x, y, element);
8953
8954       if (DONT_TOUCH(element))
8955         TestIfBadThingTouchesPlayer(x, y);
8956
8957       return;
8958     }
8959
8960     InitMovingField(x, y, MovDir[x][y]);
8961
8962     PlayLevelSoundAction(x, y, ACTION_MOVING);
8963   }
8964
8965   if (MovDir[x][y])
8966     ContinueMoving(x, y);
8967 }
8968
8969 void ContinueMoving(int x, int y)
8970 {
8971   int element = Feld[x][y];
8972   struct ElementInfo *ei = &element_info[element];
8973   int direction = MovDir[x][y];
8974   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8975   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8976   int newx = x + dx, newy = y + dy;
8977   int stored = Store[x][y];
8978   int stored_new = Store[newx][newy];
8979   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8980   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8981   boolean last_line = (newy == lev_fieldy - 1);
8982
8983   MovPos[x][y] += getElementMoveStepsize(x, y);
8984
8985   if (pushed_by_player) /* special case: moving object pushed by player */
8986     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8987
8988   if (ABS(MovPos[x][y]) < TILEX)
8989   {
8990 #if 0
8991     int ee = Feld[x][y];
8992     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8993     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8994
8995     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8996            x, y, ABS(MovPos[x][y]),
8997            ee, gg, ff,
8998            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8999 #endif
9000
9001     TEST_DrawLevelField(x, y);
9002
9003     return;     /* element is still moving */
9004   }
9005
9006   /* element reached destination field */
9007
9008   Feld[x][y] = EL_EMPTY;
9009   Feld[newx][newy] = element;
9010   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9011
9012   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9013   {
9014     element = Feld[newx][newy] = EL_ACID;
9015   }
9016   else if (element == EL_MOLE)
9017   {
9018     Feld[x][y] = EL_SAND;
9019
9020     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9021   }
9022   else if (element == EL_QUICKSAND_FILLING)
9023   {
9024     element = Feld[newx][newy] = get_next_element(element);
9025     Store[newx][newy] = Store[x][y];
9026   }
9027   else if (element == EL_QUICKSAND_EMPTYING)
9028   {
9029     Feld[x][y] = get_next_element(element);
9030     element = Feld[newx][newy] = Store[x][y];
9031   }
9032   else if (element == EL_QUICKSAND_FAST_FILLING)
9033   {
9034     element = Feld[newx][newy] = get_next_element(element);
9035     Store[newx][newy] = Store[x][y];
9036   }
9037   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9038   {
9039     Feld[x][y] = get_next_element(element);
9040     element = Feld[newx][newy] = Store[x][y];
9041   }
9042   else if (element == EL_MAGIC_WALL_FILLING)
9043   {
9044     element = Feld[newx][newy] = get_next_element(element);
9045     if (!game.magic_wall_active)
9046       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9047     Store[newx][newy] = Store[x][y];
9048   }
9049   else if (element == EL_MAGIC_WALL_EMPTYING)
9050   {
9051     Feld[x][y] = get_next_element(element);
9052     if (!game.magic_wall_active)
9053       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9054     element = Feld[newx][newy] = Store[x][y];
9055
9056 #if USE_NEW_CUSTOM_VALUE
9057     InitField(newx, newy, FALSE);
9058 #endif
9059   }
9060   else if (element == EL_BD_MAGIC_WALL_FILLING)
9061   {
9062     element = Feld[newx][newy] = get_next_element(element);
9063     if (!game.magic_wall_active)
9064       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9065     Store[newx][newy] = Store[x][y];
9066   }
9067   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9068   {
9069     Feld[x][y] = get_next_element(element);
9070     if (!game.magic_wall_active)
9071       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9072     element = Feld[newx][newy] = Store[x][y];
9073
9074 #if USE_NEW_CUSTOM_VALUE
9075     InitField(newx, newy, FALSE);
9076 #endif
9077   }
9078   else if (element == EL_DC_MAGIC_WALL_FILLING)
9079   {
9080     element = Feld[newx][newy] = get_next_element(element);
9081     if (!game.magic_wall_active)
9082       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9083     Store[newx][newy] = Store[x][y];
9084   }
9085   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9086   {
9087     Feld[x][y] = get_next_element(element);
9088     if (!game.magic_wall_active)
9089       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9090     element = Feld[newx][newy] = Store[x][y];
9091
9092 #if USE_NEW_CUSTOM_VALUE
9093     InitField(newx, newy, FALSE);
9094 #endif
9095   }
9096   else if (element == EL_AMOEBA_DROPPING)
9097   {
9098     Feld[x][y] = get_next_element(element);
9099     element = Feld[newx][newy] = Store[x][y];
9100   }
9101   else if (element == EL_SOKOBAN_OBJECT)
9102   {
9103     if (Back[x][y])
9104       Feld[x][y] = Back[x][y];
9105
9106     if (Back[newx][newy])
9107       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9108
9109     Back[x][y] = Back[newx][newy] = 0;
9110   }
9111
9112   Store[x][y] = EL_EMPTY;
9113   MovPos[x][y] = 0;
9114   MovDir[x][y] = 0;
9115   MovDelay[x][y] = 0;
9116
9117   MovDelay[newx][newy] = 0;
9118
9119   if (CAN_CHANGE_OR_HAS_ACTION(element))
9120   {
9121     /* copy element change control values to new field */
9122     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9123     ChangePage[newx][newy]  = ChangePage[x][y];
9124     ChangeCount[newx][newy] = ChangeCount[x][y];
9125     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9126   }
9127
9128 #if USE_NEW_CUSTOM_VALUE
9129   CustomValue[newx][newy] = CustomValue[x][y];
9130 #endif
9131
9132   ChangeDelay[x][y] = 0;
9133   ChangePage[x][y] = -1;
9134   ChangeCount[x][y] = 0;
9135   ChangeEvent[x][y] = -1;
9136
9137 #if USE_NEW_CUSTOM_VALUE
9138   CustomValue[x][y] = 0;
9139 #endif
9140
9141   /* copy animation control values to new field */
9142   GfxFrame[newx][newy]  = GfxFrame[x][y];
9143   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9144   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9145   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9146
9147   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9148
9149   /* some elements can leave other elements behind after moving */
9150 #if 1
9151   if (ei->move_leave_element != EL_EMPTY &&
9152       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9153       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9154 #else
9155   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9156       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9157       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9158 #endif
9159   {
9160     int move_leave_element = ei->move_leave_element;
9161
9162 #if 1
9163 #if 1
9164     /* this makes it possible to leave the removed element again */
9165     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9166       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9167 #else
9168     /* this makes it possible to leave the removed element again */
9169     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9170       move_leave_element = stored;
9171 #endif
9172 #else
9173     /* this makes it possible to leave the removed element again */
9174     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9175         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9176       move_leave_element = stored;
9177 #endif
9178
9179     Feld[x][y] = move_leave_element;
9180
9181     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9182       MovDir[x][y] = direction;
9183
9184     InitField(x, y, FALSE);
9185
9186     if (GFX_CRUMBLED(Feld[x][y]))
9187       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9188
9189     if (ELEM_IS_PLAYER(move_leave_element))
9190       RelocatePlayer(x, y, move_leave_element);
9191   }
9192
9193   /* do this after checking for left-behind element */
9194   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9195
9196   if (!CAN_MOVE(element) ||
9197       (CAN_FALL(element) && direction == MV_DOWN &&
9198        (element == EL_SPRING ||
9199         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9200         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9201     GfxDir[x][y] = MovDir[newx][newy] = 0;
9202
9203   TEST_DrawLevelField(x, y);
9204   TEST_DrawLevelField(newx, newy);
9205
9206   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9207
9208   /* prevent pushed element from moving on in pushed direction */
9209   if (pushed_by_player && CAN_MOVE(element) &&
9210       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9211       !(element_info[element].move_pattern & direction))
9212     TurnRound(newx, newy);
9213
9214   /* prevent elements on conveyor belt from moving on in last direction */
9215   if (pushed_by_conveyor && CAN_FALL(element) &&
9216       direction & MV_HORIZONTAL)
9217     MovDir[newx][newy] = 0;
9218
9219   if (!pushed_by_player)
9220   {
9221     int nextx = newx + dx, nexty = newy + dy;
9222     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9223
9224     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9225
9226     if (CAN_FALL(element) && direction == MV_DOWN)
9227       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9228
9229     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9230       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9231
9232 #if USE_FIX_IMPACT_COLLISION
9233     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9234       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9235 #endif
9236   }
9237
9238   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9239   {
9240     TestIfBadThingTouchesPlayer(newx, newy);
9241     TestIfBadThingTouchesFriend(newx, newy);
9242
9243     if (!IS_CUSTOM_ELEMENT(element))
9244       TestIfBadThingTouchesOtherBadThing(newx, newy);
9245   }
9246   else if (element == EL_PENGUIN)
9247     TestIfFriendTouchesBadThing(newx, newy);
9248
9249   if (DONT_GET_HIT_BY(element))
9250   {
9251     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9252   }
9253
9254   /* give the player one last chance (one more frame) to move away */
9255   if (CAN_FALL(element) && direction == MV_DOWN &&
9256       (last_line || (!IS_FREE(x, newy + 1) &&
9257                      (!IS_PLAYER(x, newy + 1) ||
9258                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9259     Impact(x, newy);
9260
9261   if (pushed_by_player && !game.use_change_when_pushing_bug)
9262   {
9263     int push_side = MV_DIR_OPPOSITE(direction);
9264     struct PlayerInfo *player = PLAYERINFO(x, y);
9265
9266     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9267                                player->index_bit, push_side);
9268     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9269                                         player->index_bit, push_side);
9270   }
9271
9272   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9273     MovDelay[newx][newy] = 1;
9274
9275   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9276
9277   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9278
9279 #if 0
9280   if (ChangePage[newx][newy] != -1)             /* delayed change */
9281   {
9282     int page = ChangePage[newx][newy];
9283     struct ElementChangeInfo *change = &ei->change_page[page];
9284
9285     ChangePage[newx][newy] = -1;
9286
9287     if (change->can_change)
9288     {
9289       if (ChangeElement(newx, newy, element, page))
9290       {
9291         if (change->post_change_function)
9292           change->post_change_function(newx, newy);
9293       }
9294     }
9295
9296     if (change->has_action)
9297       ExecuteCustomElementAction(newx, newy, element, page);
9298   }
9299 #endif
9300
9301   TestIfElementHitsCustomElement(newx, newy, direction);
9302   TestIfPlayerTouchesCustomElement(newx, newy);
9303   TestIfElementTouchesCustomElement(newx, newy);
9304
9305   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9306       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9307     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9308                              MV_DIR_OPPOSITE(direction));
9309 }
9310
9311 int AmoebeNachbarNr(int ax, int ay)
9312 {
9313   int i;
9314   int element = Feld[ax][ay];
9315   int group_nr = 0;
9316   static int xy[4][2] =
9317   {
9318     { 0, -1 },
9319     { -1, 0 },
9320     { +1, 0 },
9321     { 0, +1 }
9322   };
9323
9324   for (i = 0; i < NUM_DIRECTIONS; i++)
9325   {
9326     int x = ax + xy[i][0];
9327     int y = ay + xy[i][1];
9328
9329     if (!IN_LEV_FIELD(x, y))
9330       continue;
9331
9332     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9333       group_nr = AmoebaNr[x][y];
9334   }
9335
9336   return group_nr;
9337 }
9338
9339 void AmoebenVereinigen(int ax, int ay)
9340 {
9341   int i, x, y, xx, yy;
9342   int new_group_nr = AmoebaNr[ax][ay];
9343   static int xy[4][2] =
9344   {
9345     { 0, -1 },
9346     { -1, 0 },
9347     { +1, 0 },
9348     { 0, +1 }
9349   };
9350
9351   if (new_group_nr == 0)
9352     return;
9353
9354   for (i = 0; i < NUM_DIRECTIONS; i++)
9355   {
9356     x = ax + xy[i][0];
9357     y = ay + xy[i][1];
9358
9359     if (!IN_LEV_FIELD(x, y))
9360       continue;
9361
9362     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9363          Feld[x][y] == EL_BD_AMOEBA ||
9364          Feld[x][y] == EL_AMOEBA_DEAD) &&
9365         AmoebaNr[x][y] != new_group_nr)
9366     {
9367       int old_group_nr = AmoebaNr[x][y];
9368
9369       if (old_group_nr == 0)
9370         return;
9371
9372       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9373       AmoebaCnt[old_group_nr] = 0;
9374       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9375       AmoebaCnt2[old_group_nr] = 0;
9376
9377       SCAN_PLAYFIELD(xx, yy)
9378       {
9379         if (AmoebaNr[xx][yy] == old_group_nr)
9380           AmoebaNr[xx][yy] = new_group_nr;
9381       }
9382     }
9383   }
9384 }
9385
9386 void AmoebeUmwandeln(int ax, int ay)
9387 {
9388   int i, x, y;
9389
9390   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9391   {
9392     int group_nr = AmoebaNr[ax][ay];
9393
9394 #ifdef DEBUG
9395     if (group_nr == 0)
9396     {
9397       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9398       printf("AmoebeUmwandeln(): This should never happen!\n");
9399       return;
9400     }
9401 #endif
9402
9403     SCAN_PLAYFIELD(x, y)
9404     {
9405       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9406       {
9407         AmoebaNr[x][y] = 0;
9408         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9409       }
9410     }
9411
9412     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9413                             SND_AMOEBA_TURNING_TO_GEM :
9414                             SND_AMOEBA_TURNING_TO_ROCK));
9415     Bang(ax, ay);
9416   }
9417   else
9418   {
9419     static int xy[4][2] =
9420     {
9421       { 0, -1 },
9422       { -1, 0 },
9423       { +1, 0 },
9424       { 0, +1 }
9425     };
9426
9427     for (i = 0; i < NUM_DIRECTIONS; i++)
9428     {
9429       x = ax + xy[i][0];
9430       y = ay + xy[i][1];
9431
9432       if (!IN_LEV_FIELD(x, y))
9433         continue;
9434
9435       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9436       {
9437         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9438                               SND_AMOEBA_TURNING_TO_GEM :
9439                               SND_AMOEBA_TURNING_TO_ROCK));
9440         Bang(x, y);
9441       }
9442     }
9443   }
9444 }
9445
9446 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9447 {
9448   int x, y;
9449   int group_nr = AmoebaNr[ax][ay];
9450   boolean done = FALSE;
9451
9452 #ifdef DEBUG
9453   if (group_nr == 0)
9454   {
9455     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9456     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9457     return;
9458   }
9459 #endif
9460
9461   SCAN_PLAYFIELD(x, y)
9462   {
9463     if (AmoebaNr[x][y] == group_nr &&
9464         (Feld[x][y] == EL_AMOEBA_DEAD ||
9465          Feld[x][y] == EL_BD_AMOEBA ||
9466          Feld[x][y] == EL_AMOEBA_GROWING))
9467     {
9468       AmoebaNr[x][y] = 0;
9469       Feld[x][y] = new_element;
9470       InitField(x, y, FALSE);
9471       TEST_DrawLevelField(x, y);
9472       done = TRUE;
9473     }
9474   }
9475
9476   if (done)
9477     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9478                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9479                             SND_BD_AMOEBA_TURNING_TO_GEM));
9480 }
9481
9482 void AmoebeWaechst(int x, int y)
9483 {
9484   static unsigned long sound_delay = 0;
9485   static unsigned long sound_delay_value = 0;
9486
9487   if (!MovDelay[x][y])          /* start new growing cycle */
9488   {
9489     MovDelay[x][y] = 7;
9490
9491     if (DelayReached(&sound_delay, sound_delay_value))
9492     {
9493       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9494       sound_delay_value = 30;
9495     }
9496   }
9497
9498   if (MovDelay[x][y])           /* wait some time before growing bigger */
9499   {
9500     MovDelay[x][y]--;
9501     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9502     {
9503       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9504                                            6 - MovDelay[x][y]);
9505
9506       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9507     }
9508
9509     if (!MovDelay[x][y])
9510     {
9511       Feld[x][y] = Store[x][y];
9512       Store[x][y] = 0;
9513       TEST_DrawLevelField(x, y);
9514     }
9515   }
9516 }
9517
9518 void AmoebaDisappearing(int x, int y)
9519 {
9520   static unsigned long sound_delay = 0;
9521   static unsigned long sound_delay_value = 0;
9522
9523   if (!MovDelay[x][y])          /* start new shrinking cycle */
9524   {
9525     MovDelay[x][y] = 7;
9526
9527     if (DelayReached(&sound_delay, sound_delay_value))
9528       sound_delay_value = 30;
9529   }
9530
9531   if (MovDelay[x][y])           /* wait some time before shrinking */
9532   {
9533     MovDelay[x][y]--;
9534     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9535     {
9536       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9537                                            6 - MovDelay[x][y]);
9538
9539       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9540     }
9541
9542     if (!MovDelay[x][y])
9543     {
9544       Feld[x][y] = EL_EMPTY;
9545       TEST_DrawLevelField(x, y);
9546
9547       /* don't let mole enter this field in this cycle;
9548          (give priority to objects falling to this field from above) */
9549       Stop[x][y] = TRUE;
9550     }
9551   }
9552 }
9553
9554 void AmoebeAbleger(int ax, int ay)
9555 {
9556   int i;
9557   int element = Feld[ax][ay];
9558   int graphic = el2img(element);
9559   int newax = ax, neway = ay;
9560   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9561   static int xy[4][2] =
9562   {
9563     { 0, -1 },
9564     { -1, 0 },
9565     { +1, 0 },
9566     { 0, +1 }
9567   };
9568
9569   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9570   {
9571     Feld[ax][ay] = EL_AMOEBA_DEAD;
9572     TEST_DrawLevelField(ax, ay);
9573     return;
9574   }
9575
9576   if (IS_ANIMATED(graphic))
9577     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9578
9579   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9580     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9581
9582   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9583   {
9584     MovDelay[ax][ay]--;
9585     if (MovDelay[ax][ay])
9586       return;
9587   }
9588
9589   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9590   {
9591     int start = RND(4);
9592     int x = ax + xy[start][0];
9593     int y = ay + xy[start][1];
9594
9595     if (!IN_LEV_FIELD(x, y))
9596       return;
9597
9598     if (IS_FREE(x, y) ||
9599         CAN_GROW_INTO(Feld[x][y]) ||
9600         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9601         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9602     {
9603       newax = x;
9604       neway = y;
9605     }
9606
9607     if (newax == ax && neway == ay)
9608       return;
9609   }
9610   else                          /* normal or "filled" (BD style) amoeba */
9611   {
9612     int start = RND(4);
9613     boolean waiting_for_player = FALSE;
9614
9615     for (i = 0; i < NUM_DIRECTIONS; i++)
9616     {
9617       int j = (start + i) % 4;
9618       int x = ax + xy[j][0];
9619       int y = ay + xy[j][1];
9620
9621       if (!IN_LEV_FIELD(x, y))
9622         continue;
9623
9624       if (IS_FREE(x, y) ||
9625           CAN_GROW_INTO(Feld[x][y]) ||
9626           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9627           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9628       {
9629         newax = x;
9630         neway = y;
9631         break;
9632       }
9633       else if (IS_PLAYER(x, y))
9634         waiting_for_player = TRUE;
9635     }
9636
9637     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9638     {
9639       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9640       {
9641         Feld[ax][ay] = EL_AMOEBA_DEAD;
9642         TEST_DrawLevelField(ax, ay);
9643         AmoebaCnt[AmoebaNr[ax][ay]]--;
9644
9645         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9646         {
9647           if (element == EL_AMOEBA_FULL)
9648             AmoebeUmwandeln(ax, ay);
9649           else if (element == EL_BD_AMOEBA)
9650             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9651         }
9652       }
9653       return;
9654     }
9655     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9656     {
9657       /* amoeba gets larger by growing in some direction */
9658
9659       int new_group_nr = AmoebaNr[ax][ay];
9660
9661 #ifdef DEBUG
9662   if (new_group_nr == 0)
9663   {
9664     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9665     printf("AmoebeAbleger(): This should never happen!\n");
9666     return;
9667   }
9668 #endif
9669
9670       AmoebaNr[newax][neway] = new_group_nr;
9671       AmoebaCnt[new_group_nr]++;
9672       AmoebaCnt2[new_group_nr]++;
9673
9674       /* if amoeba touches other amoeba(s) after growing, unify them */
9675       AmoebenVereinigen(newax, neway);
9676
9677       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9678       {
9679         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9680         return;
9681       }
9682     }
9683   }
9684
9685   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9686       (neway == lev_fieldy - 1 && newax != ax))
9687   {
9688     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9689     Store[newax][neway] = element;
9690   }
9691   else if (neway == ay || element == EL_EMC_DRIPPER)
9692   {
9693     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9694
9695     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9696   }
9697   else
9698   {
9699     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9700     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9701     Store[ax][ay] = EL_AMOEBA_DROP;
9702     ContinueMoving(ax, ay);
9703     return;
9704   }
9705
9706   TEST_DrawLevelField(newax, neway);
9707 }
9708
9709 void Life(int ax, int ay)
9710 {
9711   int x1, y1, x2, y2;
9712   int life_time = 40;
9713   int element = Feld[ax][ay];
9714   int graphic = el2img(element);
9715   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9716                          level.biomaze);
9717   boolean changed = FALSE;
9718
9719   if (IS_ANIMATED(graphic))
9720     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9721
9722   if (Stop[ax][ay])
9723     return;
9724
9725   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9726     MovDelay[ax][ay] = life_time;
9727
9728   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9729   {
9730     MovDelay[ax][ay]--;
9731     if (MovDelay[ax][ay])
9732       return;
9733   }
9734
9735   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9736   {
9737     int xx = ax+x1, yy = ay+y1;
9738     int nachbarn = 0;
9739
9740     if (!IN_LEV_FIELD(xx, yy))
9741       continue;
9742
9743     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9744     {
9745       int x = xx+x2, y = yy+y2;
9746
9747       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9748         continue;
9749
9750       if (((Feld[x][y] == element ||
9751             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9752            !Stop[x][y]) ||
9753           (IS_FREE(x, y) && Stop[x][y]))
9754         nachbarn++;
9755     }
9756
9757     if (xx == ax && yy == ay)           /* field in the middle */
9758     {
9759       if (nachbarn < life_parameter[0] ||
9760           nachbarn > life_parameter[1])
9761       {
9762         Feld[xx][yy] = EL_EMPTY;
9763         if (!Stop[xx][yy])
9764           TEST_DrawLevelField(xx, yy);
9765         Stop[xx][yy] = TRUE;
9766         changed = TRUE;
9767       }
9768     }
9769     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9770     {                                   /* free border field */
9771       if (nachbarn >= life_parameter[2] &&
9772           nachbarn <= life_parameter[3])
9773       {
9774         Feld[xx][yy] = element;
9775         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9776         if (!Stop[xx][yy])
9777           TEST_DrawLevelField(xx, yy);
9778         Stop[xx][yy] = TRUE;
9779         changed = TRUE;
9780       }
9781     }
9782   }
9783
9784   if (changed)
9785     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9786                    SND_GAME_OF_LIFE_GROWING);
9787 }
9788
9789 static void InitRobotWheel(int x, int y)
9790 {
9791   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9792 }
9793
9794 static void RunRobotWheel(int x, int y)
9795 {
9796   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9797 }
9798
9799 static void StopRobotWheel(int x, int y)
9800 {
9801   if (ZX == x && ZY == y)
9802   {
9803     ZX = ZY = -1;
9804
9805     game.robot_wheel_active = FALSE;
9806   }
9807 }
9808
9809 static void InitTimegateWheel(int x, int y)
9810 {
9811   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9812 }
9813
9814 static void RunTimegateWheel(int x, int y)
9815 {
9816   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9817 }
9818
9819 static void InitMagicBallDelay(int x, int y)
9820 {
9821 #if 1
9822   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9823 #else
9824   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9825 #endif
9826 }
9827
9828 static void ActivateMagicBall(int bx, int by)
9829 {
9830   int x, y;
9831
9832   if (level.ball_random)
9833   {
9834     int pos_border = RND(8);    /* select one of the eight border elements */
9835     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9836     int xx = pos_content % 3;
9837     int yy = pos_content / 3;
9838
9839     x = bx - 1 + xx;
9840     y = by - 1 + yy;
9841
9842     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9843       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9844   }
9845   else
9846   {
9847     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9848     {
9849       int xx = x - bx + 1;
9850       int yy = y - by + 1;
9851
9852       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9853         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9854     }
9855   }
9856
9857   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9858 }
9859
9860 void CheckExit(int x, int y)
9861 {
9862   if (local_player->gems_still_needed > 0 ||
9863       local_player->sokobanfields_still_needed > 0 ||
9864       local_player->lights_still_needed > 0)
9865   {
9866     int element = Feld[x][y];
9867     int graphic = el2img(element);
9868
9869     if (IS_ANIMATED(graphic))
9870       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9871
9872     return;
9873   }
9874
9875   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9876     return;
9877
9878   Feld[x][y] = EL_EXIT_OPENING;
9879
9880   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9881 }
9882
9883 void CheckExitEM(int x, int y)
9884 {
9885   if (local_player->gems_still_needed > 0 ||
9886       local_player->sokobanfields_still_needed > 0 ||
9887       local_player->lights_still_needed > 0)
9888   {
9889     int element = Feld[x][y];
9890     int graphic = el2img(element);
9891
9892     if (IS_ANIMATED(graphic))
9893       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9894
9895     return;
9896   }
9897
9898   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9899     return;
9900
9901   Feld[x][y] = EL_EM_EXIT_OPENING;
9902
9903   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9904 }
9905
9906 void CheckExitSteel(int x, int y)
9907 {
9908   if (local_player->gems_still_needed > 0 ||
9909       local_player->sokobanfields_still_needed > 0 ||
9910       local_player->lights_still_needed > 0)
9911   {
9912     int element = Feld[x][y];
9913     int graphic = el2img(element);
9914
9915     if (IS_ANIMATED(graphic))
9916       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9917
9918     return;
9919   }
9920
9921   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9922     return;
9923
9924   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9925
9926   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9927 }
9928
9929 void CheckExitSteelEM(int x, int y)
9930 {
9931   if (local_player->gems_still_needed > 0 ||
9932       local_player->sokobanfields_still_needed > 0 ||
9933       local_player->lights_still_needed > 0)
9934   {
9935     int element = Feld[x][y];
9936     int graphic = el2img(element);
9937
9938     if (IS_ANIMATED(graphic))
9939       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9940
9941     return;
9942   }
9943
9944   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9945     return;
9946
9947   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9948
9949   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9950 }
9951
9952 void CheckExitSP(int x, int y)
9953 {
9954   if (local_player->gems_still_needed > 0)
9955   {
9956     int element = Feld[x][y];
9957     int graphic = el2img(element);
9958
9959     if (IS_ANIMATED(graphic))
9960       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9961
9962     return;
9963   }
9964
9965   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9966     return;
9967
9968   Feld[x][y] = EL_SP_EXIT_OPENING;
9969
9970   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9971 }
9972
9973 static void CloseAllOpenTimegates()
9974 {
9975   int x, y;
9976
9977   SCAN_PLAYFIELD(x, y)
9978   {
9979     int element = Feld[x][y];
9980
9981     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9982     {
9983       Feld[x][y] = EL_TIMEGATE_CLOSING;
9984
9985       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9986     }
9987   }
9988 }
9989
9990 void DrawTwinkleOnField(int x, int y)
9991 {
9992   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9993     return;
9994
9995   if (Feld[x][y] == EL_BD_DIAMOND)
9996     return;
9997
9998   if (MovDelay[x][y] == 0)      /* next animation frame */
9999     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10000
10001   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10002   {
10003     MovDelay[x][y]--;
10004
10005     DrawLevelElementAnimation(x, y, Feld[x][y]);
10006
10007     if (MovDelay[x][y] != 0)
10008     {
10009       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10010                                            10 - MovDelay[x][y]);
10011
10012       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10013     }
10014   }
10015 }
10016
10017 void MauerWaechst(int x, int y)
10018 {
10019   int delay = 6;
10020
10021   if (!MovDelay[x][y])          /* next animation frame */
10022     MovDelay[x][y] = 3 * delay;
10023
10024   if (MovDelay[x][y])           /* wait some time before next frame */
10025   {
10026     MovDelay[x][y]--;
10027
10028     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10029     {
10030       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10031       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10032
10033       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10034     }
10035
10036     if (!MovDelay[x][y])
10037     {
10038       if (MovDir[x][y] == MV_LEFT)
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_RIGHT)
10044       {
10045         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10046           TEST_DrawLevelField(x + 1, y);
10047       }
10048       else if (MovDir[x][y] == MV_UP)
10049       {
10050         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10051           TEST_DrawLevelField(x, y - 1);
10052       }
10053       else
10054       {
10055         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10056           TEST_DrawLevelField(x, y + 1);
10057       }
10058
10059       Feld[x][y] = Store[x][y];
10060       Store[x][y] = 0;
10061       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10062       TEST_DrawLevelField(x, y);
10063     }
10064   }
10065 }
10066
10067 void MauerAbleger(int ax, int ay)
10068 {
10069   int element = Feld[ax][ay];
10070   int graphic = el2img(element);
10071   boolean oben_frei = FALSE, unten_frei = FALSE;
10072   boolean links_frei = FALSE, rechts_frei = FALSE;
10073   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10074   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10075   boolean new_wall = FALSE;
10076
10077   if (IS_ANIMATED(graphic))
10078     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10079
10080   if (!MovDelay[ax][ay])        /* start building new wall */
10081     MovDelay[ax][ay] = 6;
10082
10083   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10084   {
10085     MovDelay[ax][ay]--;
10086     if (MovDelay[ax][ay])
10087       return;
10088   }
10089
10090   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10091     oben_frei = TRUE;
10092   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10093     unten_frei = TRUE;
10094   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10095     links_frei = TRUE;
10096   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10097     rechts_frei = TRUE;
10098
10099   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10100       element == EL_EXPANDABLE_WALL_ANY)
10101   {
10102     if (oben_frei)
10103     {
10104       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10105       Store[ax][ay-1] = element;
10106       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10107       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10108         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10109                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10110       new_wall = TRUE;
10111     }
10112     if (unten_frei)
10113     {
10114       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10115       Store[ax][ay+1] = element;
10116       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10117       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10118         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10119                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10120       new_wall = TRUE;
10121     }
10122   }
10123
10124   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10125       element == EL_EXPANDABLE_WALL_ANY ||
10126       element == EL_EXPANDABLE_WALL ||
10127       element == EL_BD_EXPANDABLE_WALL)
10128   {
10129     if (links_frei)
10130     {
10131       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10132       Store[ax-1][ay] = element;
10133       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10134       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10135         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10136                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10137       new_wall = TRUE;
10138     }
10139
10140     if (rechts_frei)
10141     {
10142       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10143       Store[ax+1][ay] = element;
10144       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10145       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10146         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10147                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10148       new_wall = TRUE;
10149     }
10150   }
10151
10152   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10153     TEST_DrawLevelField(ax, ay);
10154
10155   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10156     oben_massiv = TRUE;
10157   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10158     unten_massiv = TRUE;
10159   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10160     links_massiv = TRUE;
10161   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10162     rechts_massiv = TRUE;
10163
10164   if (((oben_massiv && unten_massiv) ||
10165        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10166        element == EL_EXPANDABLE_WALL) &&
10167       ((links_massiv && rechts_massiv) ||
10168        element == EL_EXPANDABLE_WALL_VERTICAL))
10169     Feld[ax][ay] = EL_WALL;
10170
10171   if (new_wall)
10172     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10173 }
10174
10175 void MauerAblegerStahl(int ax, int ay)
10176 {
10177   int element = Feld[ax][ay];
10178   int graphic = el2img(element);
10179   boolean oben_frei = FALSE, unten_frei = FALSE;
10180   boolean links_frei = FALSE, rechts_frei = FALSE;
10181   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10182   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10183   boolean new_wall = FALSE;
10184
10185   if (IS_ANIMATED(graphic))
10186     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10187
10188   if (!MovDelay[ax][ay])        /* start building new wall */
10189     MovDelay[ax][ay] = 6;
10190
10191   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10192   {
10193     MovDelay[ax][ay]--;
10194     if (MovDelay[ax][ay])
10195       return;
10196   }
10197
10198   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10199     oben_frei = TRUE;
10200   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10201     unten_frei = TRUE;
10202   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10203     links_frei = TRUE;
10204   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10205     rechts_frei = TRUE;
10206
10207   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10208       element == EL_EXPANDABLE_STEELWALL_ANY)
10209   {
10210     if (oben_frei)
10211     {
10212       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10213       Store[ax][ay-1] = element;
10214       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10215       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10216         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10217                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10218       new_wall = TRUE;
10219     }
10220     if (unten_frei)
10221     {
10222       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10223       Store[ax][ay+1] = element;
10224       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10225       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10226         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10227                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10228       new_wall = TRUE;
10229     }
10230   }
10231
10232   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10233       element == EL_EXPANDABLE_STEELWALL_ANY)
10234   {
10235     if (links_frei)
10236     {
10237       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10238       Store[ax-1][ay] = element;
10239       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10240       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10241         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10242                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10243       new_wall = TRUE;
10244     }
10245
10246     if (rechts_frei)
10247     {
10248       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10249       Store[ax+1][ay] = element;
10250       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10251       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10252         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10253                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10254       new_wall = TRUE;
10255     }
10256   }
10257
10258   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10259     oben_massiv = TRUE;
10260   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10261     unten_massiv = TRUE;
10262   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10263     links_massiv = TRUE;
10264   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10265     rechts_massiv = TRUE;
10266
10267   if (((oben_massiv && unten_massiv) ||
10268        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10269       ((links_massiv && rechts_massiv) ||
10270        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10271     Feld[ax][ay] = EL_STEELWALL;
10272
10273   if (new_wall)
10274     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10275 }
10276
10277 void CheckForDragon(int x, int y)
10278 {
10279   int i, j;
10280   boolean dragon_found = FALSE;
10281   static int xy[4][2] =
10282   {
10283     { 0, -1 },
10284     { -1, 0 },
10285     { +1, 0 },
10286     { 0, +1 }
10287   };
10288
10289   for (i = 0; i < NUM_DIRECTIONS; i++)
10290   {
10291     for (j = 0; j < 4; j++)
10292     {
10293       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10294
10295       if (IN_LEV_FIELD(xx, yy) &&
10296           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10297       {
10298         if (Feld[xx][yy] == EL_DRAGON)
10299           dragon_found = TRUE;
10300       }
10301       else
10302         break;
10303     }
10304   }
10305
10306   if (!dragon_found)
10307   {
10308     for (i = 0; i < NUM_DIRECTIONS; i++)
10309     {
10310       for (j = 0; j < 3; j++)
10311       {
10312         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10313   
10314         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10315         {
10316           Feld[xx][yy] = EL_EMPTY;
10317           TEST_DrawLevelField(xx, yy);
10318         }
10319         else
10320           break;
10321       }
10322     }
10323   }
10324 }
10325
10326 static void InitBuggyBase(int x, int y)
10327 {
10328   int element = Feld[x][y];
10329   int activating_delay = FRAMES_PER_SECOND / 4;
10330
10331   ChangeDelay[x][y] =
10332     (element == EL_SP_BUGGY_BASE ?
10333      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10334      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10335      activating_delay :
10336      element == EL_SP_BUGGY_BASE_ACTIVE ?
10337      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10338 }
10339
10340 static void WarnBuggyBase(int x, int y)
10341 {
10342   int i;
10343   static int xy[4][2] =
10344   {
10345     { 0, -1 },
10346     { -1, 0 },
10347     { +1, 0 },
10348     { 0, +1 }
10349   };
10350
10351   for (i = 0; i < NUM_DIRECTIONS; i++)
10352   {
10353     int xx = x + xy[i][0];
10354     int yy = y + xy[i][1];
10355
10356     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10357     {
10358       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10359
10360       break;
10361     }
10362   }
10363 }
10364
10365 static void InitTrap(int x, int y)
10366 {
10367   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10368 }
10369
10370 static void ActivateTrap(int x, int y)
10371 {
10372   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10373 }
10374
10375 static void ChangeActiveTrap(int x, int y)
10376 {
10377   int graphic = IMG_TRAP_ACTIVE;
10378
10379   /* if new animation frame was drawn, correct crumbled sand border */
10380   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10381     TEST_DrawLevelFieldCrumbled(x, y);
10382 }
10383
10384 static int getSpecialActionElement(int element, int number, int base_element)
10385 {
10386   return (element != EL_EMPTY ? element :
10387           number != -1 ? base_element + number - 1 :
10388           EL_EMPTY);
10389 }
10390
10391 static int getModifiedActionNumber(int value_old, int operator, int operand,
10392                                    int value_min, int value_max)
10393 {
10394   int value_new = (operator == CA_MODE_SET      ? operand :
10395                    operator == CA_MODE_ADD      ? value_old + operand :
10396                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10397                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10398                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10399                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10400                    value_old);
10401
10402   return (value_new < value_min ? value_min :
10403           value_new > value_max ? value_max :
10404           value_new);
10405 }
10406
10407 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10408 {
10409   struct ElementInfo *ei = &element_info[element];
10410   struct ElementChangeInfo *change = &ei->change_page[page];
10411   int target_element = change->target_element;
10412   int action_type = change->action_type;
10413   int action_mode = change->action_mode;
10414   int action_arg = change->action_arg;
10415   int action_element = change->action_element;
10416   int i;
10417
10418   if (!change->has_action)
10419     return;
10420
10421   /* ---------- determine action paramater values -------------------------- */
10422
10423   int level_time_value =
10424     (level.time > 0 ? TimeLeft :
10425      TimePlayed);
10426
10427   int action_arg_element_raw =
10428     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10429      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10430      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10431      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10432      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10433      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10434      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10435      EL_EMPTY);
10436   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10437
10438 #if 0
10439   if (action_arg_element_raw == EL_GROUP_START)
10440     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10441 #endif
10442
10443   int action_arg_direction =
10444     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10445      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10446      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10447      change->actual_trigger_side :
10448      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10449      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10450      MV_NONE);
10451
10452   int action_arg_number_min =
10453     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10454      CA_ARG_MIN);
10455
10456   int action_arg_number_max =
10457     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10458      action_type == CA_SET_LEVEL_GEMS ? 999 :
10459      action_type == CA_SET_LEVEL_TIME ? 9999 :
10460      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10461      action_type == CA_SET_CE_VALUE ? 9999 :
10462      action_type == CA_SET_CE_SCORE ? 9999 :
10463      CA_ARG_MAX);
10464
10465   int action_arg_number_reset =
10466     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10467      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10468      action_type == CA_SET_LEVEL_TIME ? level.time :
10469      action_type == CA_SET_LEVEL_SCORE ? 0 :
10470 #if USE_NEW_CUSTOM_VALUE
10471      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10472 #else
10473      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10474 #endif
10475      action_type == CA_SET_CE_SCORE ? 0 :
10476      0);
10477
10478   int action_arg_number =
10479     (action_arg <= CA_ARG_MAX ? action_arg :
10480      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10481      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10482      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10483      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10484      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10485      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10486 #if USE_NEW_CUSTOM_VALUE
10487      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10488 #else
10489      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10490 #endif
10491      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10492      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10493      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10494      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10495      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10496      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10497      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10498      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10499      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10500      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10501      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10502      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10503      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10504      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10505      -1);
10506
10507   int action_arg_number_old =
10508     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10509      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10510      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10511      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10512      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10513      0);
10514
10515   int action_arg_number_new =
10516     getModifiedActionNumber(action_arg_number_old,
10517                             action_mode, action_arg_number,
10518                             action_arg_number_min, action_arg_number_max);
10519
10520 #if 1
10521   int trigger_player_bits =
10522     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10523      change->actual_trigger_player_bits : change->trigger_player);
10524 #else
10525   int trigger_player_bits =
10526     (change->actual_trigger_player >= EL_PLAYER_1 &&
10527      change->actual_trigger_player <= EL_PLAYER_4 ?
10528      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10529      PLAYER_BITS_ANY);
10530 #endif
10531
10532   int action_arg_player_bits =
10533     (action_arg >= CA_ARG_PLAYER_1 &&
10534      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10535      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10536      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10537      PLAYER_BITS_ANY);
10538
10539   /* ---------- execute action  -------------------------------------------- */
10540
10541   switch (action_type)
10542   {
10543     case CA_NO_ACTION:
10544     {
10545       return;
10546     }
10547
10548     /* ---------- level actions  ------------------------------------------- */
10549
10550     case CA_RESTART_LEVEL:
10551     {
10552       game.restart_level = TRUE;
10553
10554       break;
10555     }
10556
10557     case CA_SHOW_ENVELOPE:
10558     {
10559       int element = getSpecialActionElement(action_arg_element,
10560                                             action_arg_number, EL_ENVELOPE_1);
10561
10562       if (IS_ENVELOPE(element))
10563         local_player->show_envelope = element;
10564
10565       break;
10566     }
10567
10568     case CA_SET_LEVEL_TIME:
10569     {
10570       if (level.time > 0)       /* only modify limited time value */
10571       {
10572         TimeLeft = action_arg_number_new;
10573
10574 #if 1
10575         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10576
10577         DisplayGameControlValues();
10578 #else
10579         DrawGameValue_Time(TimeLeft);
10580 #endif
10581
10582         if (!TimeLeft && setup.time_limit)
10583           for (i = 0; i < MAX_PLAYERS; i++)
10584             KillPlayer(&stored_player[i]);
10585       }
10586
10587       break;
10588     }
10589
10590     case CA_SET_LEVEL_SCORE:
10591     {
10592       local_player->score = action_arg_number_new;
10593
10594 #if 1
10595       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10596
10597       DisplayGameControlValues();
10598 #else
10599       DrawGameValue_Score(local_player->score);
10600 #endif
10601
10602       break;
10603     }
10604
10605     case CA_SET_LEVEL_GEMS:
10606     {
10607       local_player->gems_still_needed = action_arg_number_new;
10608
10609 #if 1
10610       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10611
10612       DisplayGameControlValues();
10613 #else
10614       DrawGameValue_Emeralds(local_player->gems_still_needed);
10615 #endif
10616
10617       break;
10618     }
10619
10620 #if !USE_PLAYER_GRAVITY
10621     case CA_SET_LEVEL_GRAVITY:
10622     {
10623       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10624                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10625                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10626                       game.gravity);
10627       break;
10628     }
10629 #endif
10630
10631     case CA_SET_LEVEL_WIND:
10632     {
10633       game.wind_direction = action_arg_direction;
10634
10635       break;
10636     }
10637
10638     case CA_SET_LEVEL_RANDOM_SEED:
10639     {
10640 #if 1
10641       /* ensure that setting a new random seed while playing is predictable */
10642       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10643 #else
10644       InitRND(action_arg_number_new);
10645 #endif
10646
10647 #if 0
10648       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10649 #endif
10650
10651 #if 0
10652       {
10653         int i;
10654
10655         printf("::: ");
10656         for (i = 0; i < 9; i++)
10657           printf("%d, ", RND(2));
10658         printf("\n");
10659       }
10660 #endif
10661
10662       break;
10663     }
10664
10665     /* ---------- player actions  ------------------------------------------ */
10666
10667     case CA_MOVE_PLAYER:
10668     {
10669       /* automatically move to the next field in specified direction */
10670       for (i = 0; i < MAX_PLAYERS; i++)
10671         if (trigger_player_bits & (1 << i))
10672           stored_player[i].programmed_action = action_arg_direction;
10673
10674       break;
10675     }
10676
10677     case CA_EXIT_PLAYER:
10678     {
10679       for (i = 0; i < MAX_PLAYERS; i++)
10680         if (action_arg_player_bits & (1 << i))
10681           PlayerWins(&stored_player[i]);
10682
10683       break;
10684     }
10685
10686     case CA_KILL_PLAYER:
10687     {
10688       for (i = 0; i < MAX_PLAYERS; i++)
10689         if (action_arg_player_bits & (1 << i))
10690           KillPlayer(&stored_player[i]);
10691
10692       break;
10693     }
10694
10695     case CA_SET_PLAYER_KEYS:
10696     {
10697       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10698       int element = getSpecialActionElement(action_arg_element,
10699                                             action_arg_number, EL_KEY_1);
10700
10701       if (IS_KEY(element))
10702       {
10703         for (i = 0; i < MAX_PLAYERS; i++)
10704         {
10705           if (trigger_player_bits & (1 << i))
10706           {
10707             stored_player[i].key[KEY_NR(element)] = key_state;
10708
10709             DrawGameDoorValues();
10710           }
10711         }
10712       }
10713
10714       break;
10715     }
10716
10717     case CA_SET_PLAYER_SPEED:
10718     {
10719 #if 0
10720       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10721 #endif
10722
10723       for (i = 0; i < MAX_PLAYERS; i++)
10724       {
10725         if (trigger_player_bits & (1 << i))
10726         {
10727           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10728
10729           if (action_arg == CA_ARG_SPEED_FASTER &&
10730               stored_player[i].cannot_move)
10731           {
10732             action_arg_number = STEPSIZE_VERY_SLOW;
10733           }
10734           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10735                    action_arg == CA_ARG_SPEED_FASTER)
10736           {
10737             action_arg_number = 2;
10738             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10739                            CA_MODE_MULTIPLY);
10740           }
10741           else if (action_arg == CA_ARG_NUMBER_RESET)
10742           {
10743             action_arg_number = level.initial_player_stepsize[i];
10744           }
10745
10746           move_stepsize =
10747             getModifiedActionNumber(move_stepsize,
10748                                     action_mode,
10749                                     action_arg_number,
10750                                     action_arg_number_min,
10751                                     action_arg_number_max);
10752
10753           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10754         }
10755       }
10756
10757       break;
10758     }
10759
10760     case CA_SET_PLAYER_SHIELD:
10761     {
10762       for (i = 0; i < MAX_PLAYERS; i++)
10763       {
10764         if (trigger_player_bits & (1 << i))
10765         {
10766           if (action_arg == CA_ARG_SHIELD_OFF)
10767           {
10768             stored_player[i].shield_normal_time_left = 0;
10769             stored_player[i].shield_deadly_time_left = 0;
10770           }
10771           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10772           {
10773             stored_player[i].shield_normal_time_left = 999999;
10774           }
10775           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10776           {
10777             stored_player[i].shield_normal_time_left = 999999;
10778             stored_player[i].shield_deadly_time_left = 999999;
10779           }
10780         }
10781       }
10782
10783       break;
10784     }
10785
10786 #if USE_PLAYER_GRAVITY
10787     case CA_SET_PLAYER_GRAVITY:
10788     {
10789       for (i = 0; i < MAX_PLAYERS; i++)
10790       {
10791         if (trigger_player_bits & (1 << i))
10792         {
10793           stored_player[i].gravity =
10794             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10795              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10796              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10797              stored_player[i].gravity);
10798         }
10799       }
10800
10801       break;
10802     }
10803 #endif
10804
10805     case CA_SET_PLAYER_ARTWORK:
10806     {
10807       for (i = 0; i < MAX_PLAYERS; i++)
10808       {
10809         if (trigger_player_bits & (1 << i))
10810         {
10811           int artwork_element = action_arg_element;
10812
10813           if (action_arg == CA_ARG_ELEMENT_RESET)
10814             artwork_element =
10815               (level.use_artwork_element[i] ? level.artwork_element[i] :
10816                stored_player[i].element_nr);
10817
10818 #if USE_GFX_RESET_PLAYER_ARTWORK
10819           if (stored_player[i].artwork_element != artwork_element)
10820             stored_player[i].Frame = 0;
10821 #endif
10822
10823           stored_player[i].artwork_element = artwork_element;
10824
10825           SetPlayerWaiting(&stored_player[i], FALSE);
10826
10827           /* set number of special actions for bored and sleeping animation */
10828           stored_player[i].num_special_action_bored =
10829             get_num_special_action(artwork_element,
10830                                    ACTION_BORING_1, ACTION_BORING_LAST);
10831           stored_player[i].num_special_action_sleeping =
10832             get_num_special_action(artwork_element,
10833                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10834         }
10835       }
10836
10837       break;
10838     }
10839
10840     case CA_SET_PLAYER_INVENTORY:
10841     {
10842       for (i = 0; i < MAX_PLAYERS; i++)
10843       {
10844         struct PlayerInfo *player = &stored_player[i];
10845         int j, k;
10846
10847         if (trigger_player_bits & (1 << i))
10848         {
10849           int inventory_element = action_arg_element;
10850
10851           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10852               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10853               action_arg == CA_ARG_ELEMENT_ACTION)
10854           {
10855             int element = inventory_element;
10856             int collect_count = element_info[element].collect_count_initial;
10857
10858             if (!IS_CUSTOM_ELEMENT(element))
10859               collect_count = 1;
10860
10861             if (collect_count == 0)
10862               player->inventory_infinite_element = element;
10863             else
10864               for (k = 0; k < collect_count; k++)
10865                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10866                   player->inventory_element[player->inventory_size++] =
10867                     element;
10868           }
10869           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10870                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10871                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10872           {
10873             if (player->inventory_infinite_element != EL_UNDEFINED &&
10874                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10875                                      action_arg_element_raw))
10876               player->inventory_infinite_element = EL_UNDEFINED;
10877
10878             for (k = 0, j = 0; j < player->inventory_size; j++)
10879             {
10880               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10881                                         action_arg_element_raw))
10882                 player->inventory_element[k++] = player->inventory_element[j];
10883             }
10884
10885             player->inventory_size = k;
10886           }
10887           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10888           {
10889             if (player->inventory_size > 0)
10890             {
10891               for (j = 0; j < player->inventory_size - 1; j++)
10892                 player->inventory_element[j] = player->inventory_element[j + 1];
10893
10894               player->inventory_size--;
10895             }
10896           }
10897           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10898           {
10899             if (player->inventory_size > 0)
10900               player->inventory_size--;
10901           }
10902           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10903           {
10904             player->inventory_infinite_element = EL_UNDEFINED;
10905             player->inventory_size = 0;
10906           }
10907           else if (action_arg == CA_ARG_INVENTORY_RESET)
10908           {
10909             player->inventory_infinite_element = EL_UNDEFINED;
10910             player->inventory_size = 0;
10911
10912             if (level.use_initial_inventory[i])
10913             {
10914               for (j = 0; j < level.initial_inventory_size[i]; j++)
10915               {
10916                 int element = level.initial_inventory_content[i][j];
10917                 int collect_count = element_info[element].collect_count_initial;
10918
10919                 if (!IS_CUSTOM_ELEMENT(element))
10920                   collect_count = 1;
10921
10922                 if (collect_count == 0)
10923                   player->inventory_infinite_element = element;
10924                 else
10925                   for (k = 0; k < collect_count; k++)
10926                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10927                       player->inventory_element[player->inventory_size++] =
10928                         element;
10929               }
10930             }
10931           }
10932         }
10933       }
10934
10935       break;
10936     }
10937
10938     /* ---------- CE actions  ---------------------------------------------- */
10939
10940     case CA_SET_CE_VALUE:
10941     {
10942 #if USE_NEW_CUSTOM_VALUE
10943       int last_ce_value = CustomValue[x][y];
10944
10945       CustomValue[x][y] = action_arg_number_new;
10946
10947       if (CustomValue[x][y] != last_ce_value)
10948       {
10949         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10950         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10951
10952         if (CustomValue[x][y] == 0)
10953         {
10954           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10955           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10956         }
10957       }
10958 #endif
10959
10960       break;
10961     }
10962
10963     case CA_SET_CE_SCORE:
10964     {
10965 #if USE_NEW_CUSTOM_VALUE
10966       int last_ce_score = ei->collect_score;
10967
10968       ei->collect_score = action_arg_number_new;
10969
10970       if (ei->collect_score != last_ce_score)
10971       {
10972         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10973         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10974
10975         if (ei->collect_score == 0)
10976         {
10977           int xx, yy;
10978
10979           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10980           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10981
10982           /*
10983             This is a very special case that seems to be a mixture between
10984             CheckElementChange() and CheckTriggeredElementChange(): while
10985             the first one only affects single elements that are triggered
10986             directly, the second one affects multiple elements in the playfield
10987             that are triggered indirectly by another element. This is a third
10988             case: Changing the CE score always affects multiple identical CEs,
10989             so every affected CE must be checked, not only the single CE for
10990             which the CE score was changed in the first place (as every instance
10991             of that CE shares the same CE score, and therefore also can change)!
10992           */
10993           SCAN_PLAYFIELD(xx, yy)
10994           {
10995             if (Feld[xx][yy] == element)
10996               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10997                                  CE_SCORE_GETS_ZERO);
10998           }
10999         }
11000       }
11001 #endif
11002
11003       break;
11004     }
11005
11006     case CA_SET_CE_ARTWORK:
11007     {
11008       int artwork_element = action_arg_element;
11009       boolean reset_frame = FALSE;
11010       int xx, yy;
11011
11012       if (action_arg == CA_ARG_ELEMENT_RESET)
11013         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11014                            element);
11015
11016       if (ei->gfx_element != artwork_element)
11017         reset_frame = TRUE;
11018
11019       ei->gfx_element = artwork_element;
11020
11021       SCAN_PLAYFIELD(xx, yy)
11022       {
11023         if (Feld[xx][yy] == element)
11024         {
11025           if (reset_frame)
11026           {
11027             ResetGfxAnimation(xx, yy);
11028             ResetRandomAnimationValue(xx, yy);
11029           }
11030
11031           TEST_DrawLevelField(xx, yy);
11032         }
11033       }
11034
11035       break;
11036     }
11037
11038     /* ---------- engine actions  ------------------------------------------ */
11039
11040     case CA_SET_ENGINE_SCAN_MODE:
11041     {
11042       InitPlayfieldScanMode(action_arg);
11043
11044       break;
11045     }
11046
11047     default:
11048       break;
11049   }
11050 }
11051
11052 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11053 {
11054   int old_element = Feld[x][y];
11055   int new_element = GetElementFromGroupElement(element);
11056   int previous_move_direction = MovDir[x][y];
11057 #if USE_NEW_CUSTOM_VALUE
11058   int last_ce_value = CustomValue[x][y];
11059 #endif
11060   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11061   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11062   boolean add_player_onto_element = (new_element_is_player &&
11063 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11064                                      /* this breaks SnakeBite when a snake is
11065                                         halfway through a door that closes */
11066                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11067                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11068 #endif
11069                                      IS_WALKABLE(old_element));
11070
11071 #if 0
11072   /* check if element under the player changes from accessible to unaccessible
11073      (needed for special case of dropping element which then changes) */
11074   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11075       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11076   {
11077     Bang(x, y);
11078
11079     return;
11080   }
11081 #endif
11082
11083   if (!add_player_onto_element)
11084   {
11085     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11086       RemoveMovingField(x, y);
11087     else
11088       RemoveField(x, y);
11089
11090     Feld[x][y] = new_element;
11091
11092 #if !USE_GFX_RESET_GFX_ANIMATION
11093     ResetGfxAnimation(x, y);
11094     ResetRandomAnimationValue(x, y);
11095 #endif
11096
11097     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11098       MovDir[x][y] = previous_move_direction;
11099
11100 #if USE_NEW_CUSTOM_VALUE
11101     if (element_info[new_element].use_last_ce_value)
11102       CustomValue[x][y] = last_ce_value;
11103 #endif
11104
11105     InitField_WithBug1(x, y, FALSE);
11106
11107     new_element = Feld[x][y];   /* element may have changed */
11108
11109 #if USE_GFX_RESET_GFX_ANIMATION
11110     ResetGfxAnimation(x, y);
11111     ResetRandomAnimationValue(x, y);
11112 #endif
11113
11114     TEST_DrawLevelField(x, y);
11115
11116     if (GFX_CRUMBLED(new_element))
11117       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11118   }
11119
11120 #if 1
11121   /* check if element under the player changes from accessible to unaccessible
11122      (needed for special case of dropping element which then changes) */
11123   /* (must be checked after creating new element for walkable group elements) */
11124 #if USE_FIX_KILLED_BY_NON_WALKABLE
11125   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11126       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11127   {
11128     Bang(x, y);
11129
11130     return;
11131   }
11132 #else
11133   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11134       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11135   {
11136     Bang(x, y);
11137
11138     return;
11139   }
11140 #endif
11141 #endif
11142
11143   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11144   if (new_element_is_player)
11145     RelocatePlayer(x, y, new_element);
11146
11147   if (is_change)
11148     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11149
11150   TestIfBadThingTouchesPlayer(x, y);
11151   TestIfPlayerTouchesCustomElement(x, y);
11152   TestIfElementTouchesCustomElement(x, y);
11153 }
11154
11155 static void CreateField(int x, int y, int element)
11156 {
11157   CreateFieldExt(x, y, element, FALSE);
11158 }
11159
11160 static void CreateElementFromChange(int x, int y, int element)
11161 {
11162   element = GET_VALID_RUNTIME_ELEMENT(element);
11163
11164 #if USE_STOP_CHANGED_ELEMENTS
11165   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11166   {
11167     int old_element = Feld[x][y];
11168
11169     /* prevent changed element from moving in same engine frame
11170        unless both old and new element can either fall or move */
11171     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11172         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11173       Stop[x][y] = TRUE;
11174   }
11175 #endif
11176
11177   CreateFieldExt(x, y, element, TRUE);
11178 }
11179
11180 static boolean ChangeElement(int x, int y, int element, int page)
11181 {
11182   struct ElementInfo *ei = &element_info[element];
11183   struct ElementChangeInfo *change = &ei->change_page[page];
11184   int ce_value = CustomValue[x][y];
11185   int ce_score = ei->collect_score;
11186   int target_element;
11187   int old_element = Feld[x][y];
11188
11189   /* always use default change event to prevent running into a loop */
11190   if (ChangeEvent[x][y] == -1)
11191     ChangeEvent[x][y] = CE_DELAY;
11192
11193   if (ChangeEvent[x][y] == CE_DELAY)
11194   {
11195     /* reset actual trigger element, trigger player and action element */
11196     change->actual_trigger_element = EL_EMPTY;
11197     change->actual_trigger_player = EL_EMPTY;
11198     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11199     change->actual_trigger_side = CH_SIDE_NONE;
11200     change->actual_trigger_ce_value = 0;
11201     change->actual_trigger_ce_score = 0;
11202   }
11203
11204   /* do not change elements more than a specified maximum number of changes */
11205   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11206     return FALSE;
11207
11208   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11209
11210   if (change->explode)
11211   {
11212     Bang(x, y);
11213
11214     return TRUE;
11215   }
11216
11217   if (change->use_target_content)
11218   {
11219     boolean complete_replace = TRUE;
11220     boolean can_replace[3][3];
11221     int xx, yy;
11222
11223     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11224     {
11225       boolean is_empty;
11226       boolean is_walkable;
11227       boolean is_diggable;
11228       boolean is_collectible;
11229       boolean is_removable;
11230       boolean is_destructible;
11231       int ex = x + xx - 1;
11232       int ey = y + yy - 1;
11233       int content_element = change->target_content.e[xx][yy];
11234       int e;
11235
11236       can_replace[xx][yy] = TRUE;
11237
11238       if (ex == x && ey == y)   /* do not check changing element itself */
11239         continue;
11240
11241       if (content_element == EL_EMPTY_SPACE)
11242       {
11243         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11244
11245         continue;
11246       }
11247
11248       if (!IN_LEV_FIELD(ex, ey))
11249       {
11250         can_replace[xx][yy] = FALSE;
11251         complete_replace = FALSE;
11252
11253         continue;
11254       }
11255
11256       e = Feld[ex][ey];
11257
11258       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11259         e = MovingOrBlocked2Element(ex, ey);
11260
11261       is_empty = (IS_FREE(ex, ey) ||
11262                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11263
11264       is_walkable     = (is_empty || IS_WALKABLE(e));
11265       is_diggable     = (is_empty || IS_DIGGABLE(e));
11266       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11267       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11268       is_removable    = (is_diggable || is_collectible);
11269
11270       can_replace[xx][yy] =
11271         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11272           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11273           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11274           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11275           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11276           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11277          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11278
11279       if (!can_replace[xx][yy])
11280         complete_replace = FALSE;
11281     }
11282
11283     if (!change->only_if_complete || complete_replace)
11284     {
11285       boolean something_has_changed = FALSE;
11286
11287       if (change->only_if_complete && change->use_random_replace &&
11288           RND(100) < change->random_percentage)
11289         return FALSE;
11290
11291       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11292       {
11293         int ex = x + xx - 1;
11294         int ey = y + yy - 1;
11295         int content_element;
11296
11297         if (can_replace[xx][yy] && (!change->use_random_replace ||
11298                                     RND(100) < change->random_percentage))
11299         {
11300           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11301             RemoveMovingField(ex, ey);
11302
11303           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11304
11305           content_element = change->target_content.e[xx][yy];
11306           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11307                                               ce_value, ce_score);
11308
11309           CreateElementFromChange(ex, ey, target_element);
11310
11311           something_has_changed = TRUE;
11312
11313           /* for symmetry reasons, freeze newly created border elements */
11314           if (ex != x || ey != y)
11315             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11316         }
11317       }
11318
11319       if (something_has_changed)
11320       {
11321         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11322         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11323       }
11324     }
11325   }
11326   else
11327   {
11328     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11329                                         ce_value, ce_score);
11330
11331     if (element == EL_DIAGONAL_GROWING ||
11332         element == EL_DIAGONAL_SHRINKING)
11333     {
11334       target_element = Store[x][y];
11335
11336       Store[x][y] = EL_EMPTY;
11337     }
11338
11339     CreateElementFromChange(x, y, target_element);
11340
11341     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11342     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11343   }
11344
11345   /* this uses direct change before indirect change */
11346   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11347
11348   return TRUE;
11349 }
11350
11351 #if USE_NEW_DELAYED_ACTION
11352
11353 static void HandleElementChange(int x, int y, int page)
11354 {
11355   int element = MovingOrBlocked2Element(x, y);
11356   struct ElementInfo *ei = &element_info[element];
11357   struct ElementChangeInfo *change = &ei->change_page[page];
11358   boolean handle_action_before_change = FALSE;
11359
11360 #ifdef DEBUG
11361   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11362       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11363   {
11364     printf("\n\n");
11365     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11366            x, y, element, element_info[element].token_name);
11367     printf("HandleElementChange(): This should never happen!\n");
11368     printf("\n\n");
11369   }
11370 #endif
11371
11372   /* this can happen with classic bombs on walkable, changing elements */
11373   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11374   {
11375 #if 0
11376     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11377       ChangeDelay[x][y] = 0;
11378 #endif
11379
11380     return;
11381   }
11382
11383   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11384   {
11385     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11386
11387     if (change->can_change)
11388     {
11389 #if 1
11390       /* !!! not clear why graphic animation should be reset at all here !!! */
11391       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11392 #if USE_GFX_RESET_WHEN_NOT_MOVING
11393       /* when a custom element is about to change (for example by change delay),
11394          do not reset graphic animation when the custom element is moving */
11395       if (!IS_MOVING(x, y))
11396 #endif
11397       {
11398         ResetGfxAnimation(x, y);
11399         ResetRandomAnimationValue(x, y);
11400       }
11401 #endif
11402
11403       if (change->pre_change_function)
11404         change->pre_change_function(x, y);
11405     }
11406   }
11407
11408   ChangeDelay[x][y]--;
11409
11410   if (ChangeDelay[x][y] != 0)           /* continue element change */
11411   {
11412     if (change->can_change)
11413     {
11414       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11415
11416       if (IS_ANIMATED(graphic))
11417         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11418
11419       if (change->change_function)
11420         change->change_function(x, y);
11421     }
11422   }
11423   else                                  /* finish element change */
11424   {
11425     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11426     {
11427       page = ChangePage[x][y];
11428       ChangePage[x][y] = -1;
11429
11430       change = &ei->change_page[page];
11431     }
11432
11433     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11434     {
11435       ChangeDelay[x][y] = 1;            /* try change after next move step */
11436       ChangePage[x][y] = page;          /* remember page to use for change */
11437
11438       return;
11439     }
11440
11441 #if 1
11442     /* special case: set new level random seed before changing element */
11443     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11444       handle_action_before_change = TRUE;
11445
11446     if (change->has_action && handle_action_before_change)
11447       ExecuteCustomElementAction(x, y, element, page);
11448 #endif
11449
11450     if (change->can_change)
11451     {
11452       if (ChangeElement(x, y, element, page))
11453       {
11454         if (change->post_change_function)
11455           change->post_change_function(x, y);
11456       }
11457     }
11458
11459     if (change->has_action && !handle_action_before_change)
11460       ExecuteCustomElementAction(x, y, element, page);
11461   }
11462 }
11463
11464 #else
11465
11466 static void HandleElementChange(int x, int y, int page)
11467 {
11468   int element = MovingOrBlocked2Element(x, y);
11469   struct ElementInfo *ei = &element_info[element];
11470   struct ElementChangeInfo *change = &ei->change_page[page];
11471
11472 #ifdef DEBUG
11473   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11474   {
11475     printf("\n\n");
11476     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11477            x, y, element, element_info[element].token_name);
11478     printf("HandleElementChange(): This should never happen!\n");
11479     printf("\n\n");
11480   }
11481 #endif
11482
11483   /* this can happen with classic bombs on walkable, changing elements */
11484   if (!CAN_CHANGE(element))
11485   {
11486 #if 0
11487     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11488       ChangeDelay[x][y] = 0;
11489 #endif
11490
11491     return;
11492   }
11493
11494   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11495   {
11496     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11497
11498     ResetGfxAnimation(x, y);
11499     ResetRandomAnimationValue(x, y);
11500
11501     if (change->pre_change_function)
11502       change->pre_change_function(x, y);
11503   }
11504
11505   ChangeDelay[x][y]--;
11506
11507   if (ChangeDelay[x][y] != 0)           /* continue element change */
11508   {
11509     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11510
11511     if (IS_ANIMATED(graphic))
11512       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11513
11514     if (change->change_function)
11515       change->change_function(x, y);
11516   }
11517   else                                  /* finish element change */
11518   {
11519     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11520     {
11521       page = ChangePage[x][y];
11522       ChangePage[x][y] = -1;
11523
11524       change = &ei->change_page[page];
11525     }
11526
11527     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11528     {
11529       ChangeDelay[x][y] = 1;            /* try change after next move step */
11530       ChangePage[x][y] = page;          /* remember page to use for change */
11531
11532       return;
11533     }
11534
11535     if (ChangeElement(x, y, element, page))
11536     {
11537       if (change->post_change_function)
11538         change->post_change_function(x, y);
11539     }
11540   }
11541 }
11542
11543 #endif
11544
11545 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11546                                               int trigger_element,
11547                                               int trigger_event,
11548                                               int trigger_player,
11549                                               int trigger_side,
11550                                               int trigger_page)
11551 {
11552   boolean change_done_any = FALSE;
11553   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11554   int i;
11555
11556   if (!(trigger_events[trigger_element][trigger_event]))
11557     return FALSE;
11558
11559 #if 0
11560   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11561          trigger_event, recursion_loop_depth, recursion_loop_detected,
11562          recursion_loop_element, EL_NAME(recursion_loop_element));
11563 #endif
11564
11565   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11566
11567   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11568   {
11569     int element = EL_CUSTOM_START + i;
11570     boolean change_done = FALSE;
11571     int p;
11572
11573     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11574         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11575       continue;
11576
11577     for (p = 0; p < element_info[element].num_change_pages; p++)
11578     {
11579       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11580
11581       if (change->can_change_or_has_action &&
11582           change->has_event[trigger_event] &&
11583           change->trigger_side & trigger_side &&
11584           change->trigger_player & trigger_player &&
11585           change->trigger_page & trigger_page_bits &&
11586           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11587       {
11588         change->actual_trigger_element = trigger_element;
11589         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11590         change->actual_trigger_player_bits = trigger_player;
11591         change->actual_trigger_side = trigger_side;
11592         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11593         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11594
11595 #if 0
11596         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11597                element, EL_NAME(element), p);
11598 #endif
11599
11600         if ((change->can_change && !change_done) || change->has_action)
11601         {
11602           int x, y;
11603
11604           SCAN_PLAYFIELD(x, y)
11605           {
11606             if (Feld[x][y] == element)
11607             {
11608               if (change->can_change && !change_done)
11609               {
11610 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11611                 /* if element already changed in this frame, not only prevent
11612                    another element change (checked in ChangeElement()), but
11613                    also prevent additional element actions for this element */
11614
11615                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11616                     !level.use_action_after_change_bug)
11617                   continue;
11618 #endif
11619
11620 #if 0
11621                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11622                        element, EL_NAME(element), p);
11623 #endif
11624
11625                 ChangeDelay[x][y] = 1;
11626                 ChangeEvent[x][y] = trigger_event;
11627
11628                 HandleElementChange(x, y, p);
11629               }
11630 #if USE_NEW_DELAYED_ACTION
11631               else if (change->has_action)
11632               {
11633 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11634                 /* if element already changed in this frame, not only prevent
11635                    another element change (checked in ChangeElement()), but
11636                    also prevent additional element actions for this element */
11637
11638                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11639                     !level.use_action_after_change_bug)
11640                   continue;
11641 #endif
11642
11643
11644 #if 0
11645                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11646                        element, EL_NAME(element), p);
11647 #endif
11648
11649                 ExecuteCustomElementAction(x, y, element, p);
11650                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11651               }
11652 #else
11653               if (change->has_action)
11654               {
11655                 ExecuteCustomElementAction(x, y, element, p);
11656                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11657               }
11658 #endif
11659             }
11660           }
11661
11662           if (change->can_change)
11663           {
11664             change_done = TRUE;
11665             change_done_any = TRUE;
11666
11667 #if 0
11668             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11669                    element, EL_NAME(element), p);
11670 #endif
11671
11672           }
11673         }
11674       }
11675     }
11676   }
11677
11678   RECURSION_LOOP_DETECTION_END();
11679
11680   return change_done_any;
11681 }
11682
11683 static boolean CheckElementChangeExt(int x, int y,
11684                                      int element,
11685                                      int trigger_element,
11686                                      int trigger_event,
11687                                      int trigger_player,
11688                                      int trigger_side)
11689 {
11690   boolean change_done = FALSE;
11691   int p;
11692
11693   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11694       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11695     return FALSE;
11696
11697   if (Feld[x][y] == EL_BLOCKED)
11698   {
11699     Blocked2Moving(x, y, &x, &y);
11700     element = Feld[x][y];
11701   }
11702
11703 #if 0
11704   /* check if element has already changed */
11705   if (Feld[x][y] != element)
11706     return FALSE;
11707 #else
11708   /* check if element has already changed or is about to change after moving */
11709   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11710        Feld[x][y] != element) ||
11711
11712       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11713        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11714         ChangePage[x][y] != -1)))
11715     return FALSE;
11716 #endif
11717
11718 #if 0
11719   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11720          trigger_event, recursion_loop_depth, recursion_loop_detected,
11721          recursion_loop_element, EL_NAME(recursion_loop_element));
11722 #endif
11723
11724   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11725
11726 #if 0
11727   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11728 #endif
11729
11730   for (p = 0; p < element_info[element].num_change_pages; p++)
11731   {
11732     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11733
11734     /* check trigger element for all events where the element that is checked
11735        for changing interacts with a directly adjacent element -- this is
11736        different to element changes that affect other elements to change on the
11737        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11738     boolean check_trigger_element =
11739       (trigger_event == CE_TOUCHING_X ||
11740        trigger_event == CE_HITTING_X ||
11741        trigger_event == CE_HIT_BY_X ||
11742 #if 1
11743        /* this one was forgotten until 3.2.3 */
11744        trigger_event == CE_DIGGING_X);
11745 #endif
11746
11747     if (change->can_change_or_has_action &&
11748         change->has_event[trigger_event] &&
11749         change->trigger_side & trigger_side &&
11750         change->trigger_player & trigger_player &&
11751         (!check_trigger_element ||
11752          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11753     {
11754       change->actual_trigger_element = trigger_element;
11755       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11756       change->actual_trigger_player_bits = trigger_player;
11757       change->actual_trigger_side = trigger_side;
11758       change->actual_trigger_ce_value = CustomValue[x][y];
11759       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11760
11761       /* special case: trigger element not at (x,y) position for some events */
11762       if (check_trigger_element)
11763       {
11764         static struct
11765         {
11766           int dx, dy;
11767         } move_xy[] =
11768           {
11769             {  0,  0 },
11770             { -1,  0 },
11771             { +1,  0 },
11772             {  0,  0 },
11773             {  0, -1 },
11774             {  0,  0 }, { 0, 0 }, { 0, 0 },
11775             {  0, +1 }
11776           };
11777
11778         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11779         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11780
11781         change->actual_trigger_ce_value = CustomValue[xx][yy];
11782         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11783       }
11784
11785       if (change->can_change && !change_done)
11786       {
11787         ChangeDelay[x][y] = 1;
11788         ChangeEvent[x][y] = trigger_event;
11789
11790         HandleElementChange(x, y, p);
11791
11792         change_done = TRUE;
11793       }
11794 #if USE_NEW_DELAYED_ACTION
11795       else if (change->has_action)
11796       {
11797         ExecuteCustomElementAction(x, y, element, p);
11798         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11799       }
11800 #else
11801       if (change->has_action)
11802       {
11803         ExecuteCustomElementAction(x, y, element, p);
11804         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11805       }
11806 #endif
11807     }
11808   }
11809
11810   RECURSION_LOOP_DETECTION_END();
11811
11812   return change_done;
11813 }
11814
11815 static void PlayPlayerSound(struct PlayerInfo *player)
11816 {
11817   int jx = player->jx, jy = player->jy;
11818   int sound_element = player->artwork_element;
11819   int last_action = player->last_action_waiting;
11820   int action = player->action_waiting;
11821
11822   if (player->is_waiting)
11823   {
11824     if (action != last_action)
11825       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11826     else
11827       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11828   }
11829   else
11830   {
11831     if (action != last_action)
11832       StopSound(element_info[sound_element].sound[last_action]);
11833
11834     if (last_action == ACTION_SLEEPING)
11835       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11836   }
11837 }
11838
11839 static void PlayAllPlayersSound()
11840 {
11841   int i;
11842
11843   for (i = 0; i < MAX_PLAYERS; i++)
11844     if (stored_player[i].active)
11845       PlayPlayerSound(&stored_player[i]);
11846 }
11847
11848 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11849 {
11850   boolean last_waiting = player->is_waiting;
11851   int move_dir = player->MovDir;
11852
11853   player->dir_waiting = move_dir;
11854   player->last_action_waiting = player->action_waiting;
11855
11856   if (is_waiting)
11857   {
11858     if (!last_waiting)          /* not waiting -> waiting */
11859     {
11860       player->is_waiting = TRUE;
11861
11862       player->frame_counter_bored =
11863         FrameCounter +
11864         game.player_boring_delay_fixed +
11865         GetSimpleRandom(game.player_boring_delay_random);
11866       player->frame_counter_sleeping =
11867         FrameCounter +
11868         game.player_sleeping_delay_fixed +
11869         GetSimpleRandom(game.player_sleeping_delay_random);
11870
11871       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11872     }
11873
11874     if (game.player_sleeping_delay_fixed +
11875         game.player_sleeping_delay_random > 0 &&
11876         player->anim_delay_counter == 0 &&
11877         player->post_delay_counter == 0 &&
11878         FrameCounter >= player->frame_counter_sleeping)
11879       player->is_sleeping = TRUE;
11880     else if (game.player_boring_delay_fixed +
11881              game.player_boring_delay_random > 0 &&
11882              FrameCounter >= player->frame_counter_bored)
11883       player->is_bored = TRUE;
11884
11885     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11886                               player->is_bored ? ACTION_BORING :
11887                               ACTION_WAITING);
11888
11889     if (player->is_sleeping && player->use_murphy)
11890     {
11891       /* special case for sleeping Murphy when leaning against non-free tile */
11892
11893       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11894           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11895            !IS_MOVING(player->jx - 1, player->jy)))
11896         move_dir = MV_LEFT;
11897       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11898                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11899                 !IS_MOVING(player->jx + 1, player->jy)))
11900         move_dir = MV_RIGHT;
11901       else
11902         player->is_sleeping = FALSE;
11903
11904       player->dir_waiting = move_dir;
11905     }
11906
11907     if (player->is_sleeping)
11908     {
11909       if (player->num_special_action_sleeping > 0)
11910       {
11911         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11912         {
11913           int last_special_action = player->special_action_sleeping;
11914           int num_special_action = player->num_special_action_sleeping;
11915           int special_action =
11916             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11917              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11918              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11919              last_special_action + 1 : ACTION_SLEEPING);
11920           int special_graphic =
11921             el_act_dir2img(player->artwork_element, special_action, move_dir);
11922
11923           player->anim_delay_counter =
11924             graphic_info[special_graphic].anim_delay_fixed +
11925             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11926           player->post_delay_counter =
11927             graphic_info[special_graphic].post_delay_fixed +
11928             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11929
11930           player->special_action_sleeping = special_action;
11931         }
11932
11933         if (player->anim_delay_counter > 0)
11934         {
11935           player->action_waiting = player->special_action_sleeping;
11936           player->anim_delay_counter--;
11937         }
11938         else if (player->post_delay_counter > 0)
11939         {
11940           player->post_delay_counter--;
11941         }
11942       }
11943     }
11944     else if (player->is_bored)
11945     {
11946       if (player->num_special_action_bored > 0)
11947       {
11948         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11949         {
11950           int special_action =
11951             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11952           int special_graphic =
11953             el_act_dir2img(player->artwork_element, special_action, move_dir);
11954
11955           player->anim_delay_counter =
11956             graphic_info[special_graphic].anim_delay_fixed +
11957             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11958           player->post_delay_counter =
11959             graphic_info[special_graphic].post_delay_fixed +
11960             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11961
11962           player->special_action_bored = special_action;
11963         }
11964
11965         if (player->anim_delay_counter > 0)
11966         {
11967           player->action_waiting = player->special_action_bored;
11968           player->anim_delay_counter--;
11969         }
11970         else if (player->post_delay_counter > 0)
11971         {
11972           player->post_delay_counter--;
11973         }
11974       }
11975     }
11976   }
11977   else if (last_waiting)        /* waiting -> not waiting */
11978   {
11979     player->is_waiting = FALSE;
11980     player->is_bored = FALSE;
11981     player->is_sleeping = FALSE;
11982
11983     player->frame_counter_bored = -1;
11984     player->frame_counter_sleeping = -1;
11985
11986     player->anim_delay_counter = 0;
11987     player->post_delay_counter = 0;
11988
11989     player->dir_waiting = player->MovDir;
11990     player->action_waiting = ACTION_DEFAULT;
11991
11992     player->special_action_bored = ACTION_DEFAULT;
11993     player->special_action_sleeping = ACTION_DEFAULT;
11994   }
11995 }
11996
11997 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11998 {
11999   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12000   int left      = player_action & JOY_LEFT;
12001   int right     = player_action & JOY_RIGHT;
12002   int up        = player_action & JOY_UP;
12003   int down      = player_action & JOY_DOWN;
12004   int button1   = player_action & JOY_BUTTON_1;
12005   int button2   = player_action & JOY_BUTTON_2;
12006   int dx        = (left ? -1 : right ? 1 : 0);
12007   int dy        = (up   ? -1 : down  ? 1 : 0);
12008
12009   if (!player->active || tape.pausing)
12010     return 0;
12011
12012   if (player_action)
12013   {
12014     if (button1)
12015       snapped = SnapField(player, dx, dy);
12016     else
12017     {
12018       if (button2)
12019         dropped = DropElement(player);
12020
12021       moved = MovePlayer(player, dx, dy);
12022     }
12023
12024     if (tape.single_step && tape.recording && !tape.pausing)
12025     {
12026 #if 1
12027       /* as it is called "single step mode", just return to pause mode when the
12028          player stopped moving after one tile (or never starts moving at all) */
12029       if (!player->is_moving)
12030 #else
12031       /* this is buggy: there are quite some cases where the single step mode
12032          does not return to pause mode (like pushing things that don't move
12033          or simply by trying to run against a wall) */
12034       if (button1 || (dropped && !moved))
12035 #endif
12036       {
12037         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12038         SnapField(player, 0, 0);                /* stop snapping */
12039       }
12040     }
12041
12042     SetPlayerWaiting(player, FALSE);
12043
12044     return player_action;
12045   }
12046   else
12047   {
12048     /* no actions for this player (no input at player's configured device) */
12049
12050     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12051     SnapField(player, 0, 0);
12052     CheckGravityMovementWhenNotMoving(player);
12053
12054     if (player->MovPos == 0)
12055       SetPlayerWaiting(player, TRUE);
12056
12057     if (player->MovPos == 0)    /* needed for tape.playing */
12058       player->is_moving = FALSE;
12059
12060     player->is_dropping = FALSE;
12061     player->is_dropping_pressed = FALSE;
12062     player->drop_pressed_delay = 0;
12063
12064     return 0;
12065   }
12066 }
12067
12068 static void CheckLevelTime()
12069 {
12070   int i;
12071
12072   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12073   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12074   {
12075     if (level.native_em_level->lev->home == 0)  /* all players at home */
12076     {
12077       PlayerWins(local_player);
12078
12079       AllPlayersGone = TRUE;
12080
12081       level.native_em_level->lev->home = -1;
12082     }
12083
12084     if (level.native_em_level->ply[0]->alive == 0 &&
12085         level.native_em_level->ply[1]->alive == 0 &&
12086         level.native_em_level->ply[2]->alive == 0 &&
12087         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12088       AllPlayersGone = TRUE;
12089   }
12090   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12091   {
12092     if (game_sp.LevelSolved &&
12093         !game_sp.GameOver)                              /* game won */
12094     {
12095       PlayerWins(local_player);
12096
12097       game_sp.GameOver = TRUE;
12098
12099       AllPlayersGone = TRUE;
12100     }
12101
12102     if (game_sp.GameOver)                               /* game lost */
12103       AllPlayersGone = TRUE;
12104   }
12105
12106   if (TimeFrames >= FRAMES_PER_SECOND)
12107   {
12108     TimeFrames = 0;
12109     TapeTime++;
12110
12111     for (i = 0; i < MAX_PLAYERS; i++)
12112     {
12113       struct PlayerInfo *player = &stored_player[i];
12114
12115       if (SHIELD_ON(player))
12116       {
12117         player->shield_normal_time_left--;
12118
12119         if (player->shield_deadly_time_left > 0)
12120           player->shield_deadly_time_left--;
12121       }
12122     }
12123
12124     if (!local_player->LevelSolved && !level.use_step_counter)
12125     {
12126       TimePlayed++;
12127
12128       if (TimeLeft > 0)
12129       {
12130         TimeLeft--;
12131
12132         if (TimeLeft <= 10 && setup.time_limit)
12133           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12134
12135 #if 1
12136         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12137
12138         DisplayGameControlValues();
12139 #else
12140         DrawGameValue_Time(TimeLeft);
12141 #endif
12142
12143         if (!TimeLeft && setup.time_limit)
12144         {
12145           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12146             level.native_em_level->lev->killed_out_of_time = TRUE;
12147           else
12148             for (i = 0; i < MAX_PLAYERS; i++)
12149               KillPlayer(&stored_player[i]);
12150         }
12151       }
12152 #if 1
12153       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12154       {
12155         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12156
12157         DisplayGameControlValues();
12158       }
12159 #else
12160       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12161         DrawGameValue_Time(TimePlayed);
12162 #endif
12163
12164       level.native_em_level->lev->time =
12165         (level.time == 0 ? TimePlayed : TimeLeft);
12166     }
12167
12168     if (tape.recording || tape.playing)
12169       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12170   }
12171
12172 #if 1
12173   UpdateAndDisplayGameControlValues();
12174 #else
12175   UpdateGameDoorValues();
12176   DrawGameDoorValues();
12177 #endif
12178 }
12179
12180 void AdvanceFrameAndPlayerCounters(int player_nr)
12181 {
12182   int i;
12183
12184   /* advance frame counters (global frame counter and time frame counter) */
12185   FrameCounter++;
12186   TimeFrames++;
12187
12188   /* advance player counters (counters for move delay, move animation etc.) */
12189   for (i = 0; i < MAX_PLAYERS; i++)
12190   {
12191     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12192     int move_delay_value = stored_player[i].move_delay_value;
12193     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12194
12195     if (!advance_player_counters)       /* not all players may be affected */
12196       continue;
12197
12198 #if USE_NEW_PLAYER_ANIM
12199     if (move_frames == 0)       /* less than one move per game frame */
12200     {
12201       int stepsize = TILEX / move_delay_value;
12202       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12203       int count = (stored_player[i].is_moving ?
12204                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12205
12206       if (count % delay == 0)
12207         move_frames = 1;
12208     }
12209 #endif
12210
12211     stored_player[i].Frame += move_frames;
12212
12213     if (stored_player[i].MovPos != 0)
12214       stored_player[i].StepFrame += move_frames;
12215
12216     if (stored_player[i].move_delay > 0)
12217       stored_player[i].move_delay--;
12218
12219     /* due to bugs in previous versions, counter must count up, not down */
12220     if (stored_player[i].push_delay != -1)
12221       stored_player[i].push_delay++;
12222
12223     if (stored_player[i].drop_delay > 0)
12224       stored_player[i].drop_delay--;
12225
12226     if (stored_player[i].is_dropping_pressed)
12227       stored_player[i].drop_pressed_delay++;
12228   }
12229 }
12230
12231 void StartGameActions(boolean init_network_game, boolean record_tape,
12232                       long random_seed)
12233 {
12234   unsigned long new_random_seed = InitRND(random_seed);
12235
12236   if (record_tape)
12237     TapeStartRecording(new_random_seed);
12238
12239 #if defined(NETWORK_AVALIABLE)
12240   if (init_network_game)
12241   {
12242     SendToServer_StartPlaying();
12243
12244     return;
12245   }
12246 #endif
12247
12248   InitGame();
12249 }
12250
12251 void GameActions()
12252 {
12253   static unsigned long game_frame_delay = 0;
12254   unsigned long game_frame_delay_value;
12255   byte *recorded_player_action;
12256   byte summarized_player_action = 0;
12257   byte tape_action[MAX_PLAYERS];
12258   int i;
12259
12260   /* detect endless loops, caused by custom element programming */
12261   if (recursion_loop_detected && recursion_loop_depth == 0)
12262   {
12263     char *message = getStringCat3("Internal Error ! Element ",
12264                                   EL_NAME(recursion_loop_element),
12265                                   " caused endless loop ! Quit the game ?");
12266
12267     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12268           EL_NAME(recursion_loop_element));
12269
12270     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12271
12272     recursion_loop_detected = FALSE;    /* if game should be continued */
12273
12274     free(message);
12275
12276     return;
12277   }
12278
12279   if (game.restart_level)
12280     StartGameActions(options.network, setup.autorecord, level.random_seed);
12281
12282   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12283   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12284   {
12285     if (level.native_em_level->lev->home == 0)  /* all players at home */
12286     {
12287       PlayerWins(local_player);
12288
12289       AllPlayersGone = TRUE;
12290
12291       level.native_em_level->lev->home = -1;
12292     }
12293
12294     if (level.native_em_level->ply[0]->alive == 0 &&
12295         level.native_em_level->ply[1]->alive == 0 &&
12296         level.native_em_level->ply[2]->alive == 0 &&
12297         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12298       AllPlayersGone = TRUE;
12299   }
12300   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12301   {
12302     if (game_sp.LevelSolved &&
12303         !game_sp.GameOver)                              /* game won */
12304     {
12305       PlayerWins(local_player);
12306
12307       game_sp.GameOver = TRUE;
12308
12309       AllPlayersGone = TRUE;
12310     }
12311
12312     if (game_sp.GameOver)                               /* game lost */
12313       AllPlayersGone = TRUE;
12314   }
12315
12316   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12317     GameWon();
12318
12319   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12320     TapeStop();
12321
12322   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12323     return;
12324
12325   game_frame_delay_value =
12326     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12327
12328   if (tape.playing && tape.warp_forward && !tape.pausing)
12329     game_frame_delay_value = 0;
12330
12331   /* ---------- main game synchronization point ---------- */
12332
12333   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12334
12335   if (network_playing && !network_player_action_received)
12336   {
12337     /* try to get network player actions in time */
12338
12339 #if defined(NETWORK_AVALIABLE)
12340     /* last chance to get network player actions without main loop delay */
12341     HandleNetworking();
12342 #endif
12343
12344     /* game was quit by network peer */
12345     if (game_status != GAME_MODE_PLAYING)
12346       return;
12347
12348     if (!network_player_action_received)
12349       return;           /* failed to get network player actions in time */
12350
12351     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12352   }
12353
12354   if (tape.pausing)
12355     return;
12356
12357   /* at this point we know that we really continue executing the game */
12358
12359   network_player_action_received = FALSE;
12360
12361   /* when playing tape, read previously recorded player input from tape data */
12362   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12363
12364 #if 1
12365   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12366   if (tape.pausing)
12367     return;
12368 #endif
12369
12370   if (tape.set_centered_player)
12371   {
12372     game.centered_player_nr_next = tape.centered_player_nr_next;
12373     game.set_centered_player = TRUE;
12374   }
12375
12376   for (i = 0; i < MAX_PLAYERS; i++)
12377   {
12378     summarized_player_action |= stored_player[i].action;
12379
12380     if (!network_playing)
12381       stored_player[i].effective_action = stored_player[i].action;
12382   }
12383
12384 #if defined(NETWORK_AVALIABLE)
12385   if (network_playing)
12386     SendToServer_MovePlayer(summarized_player_action);
12387 #endif
12388
12389   if (!options.network && !setup.team_mode)
12390     local_player->effective_action = summarized_player_action;
12391
12392   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12393   {
12394     for (i = 0; i < MAX_PLAYERS; i++)
12395       stored_player[i].effective_action =
12396         (i == game.centered_player_nr ? summarized_player_action : 0);
12397   }
12398
12399   if (recorded_player_action != NULL)
12400     for (i = 0; i < MAX_PLAYERS; i++)
12401       stored_player[i].effective_action = recorded_player_action[i];
12402
12403   for (i = 0; i < MAX_PLAYERS; i++)
12404   {
12405     tape_action[i] = stored_player[i].effective_action;
12406
12407     /* (this can only happen in the R'n'D game engine) */
12408     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12409       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12410   }
12411
12412   /* only record actions from input devices, but not programmed actions */
12413   if (tape.recording)
12414     TapeRecordAction(tape_action);
12415
12416 #if USE_NEW_PLAYER_ASSIGNMENTS
12417   {
12418     byte mapped_action[MAX_PLAYERS];
12419
12420     for (i = 0; i < MAX_PLAYERS; i++)
12421       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12422
12423     for (i = 0; i < MAX_PLAYERS; i++)
12424       stored_player[i].effective_action = mapped_action[i];
12425   }
12426 #endif
12427
12428   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12429   {
12430     GameActions_EM_Main();
12431   }
12432   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12433   {
12434     GameActions_SP_Main();
12435   }
12436   else
12437   {
12438     GameActions_RND();
12439   }
12440 }
12441
12442 void GameActions_EM_Main()
12443 {
12444   byte effective_action[MAX_PLAYERS];
12445   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12446   int i;
12447
12448   for (i = 0; i < MAX_PLAYERS; i++)
12449     effective_action[i] = stored_player[i].effective_action;
12450
12451   GameActions_EM(effective_action, warp_mode);
12452
12453   CheckLevelTime();
12454
12455   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12456 }
12457
12458 void GameActions_SP_Main()
12459 {
12460   byte effective_action[MAX_PLAYERS];
12461   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12462   int i;
12463
12464   for (i = 0; i < MAX_PLAYERS; i++)
12465     effective_action[i] = stored_player[i].effective_action;
12466
12467   GameActions_SP(effective_action, warp_mode);
12468
12469   CheckLevelTime();
12470
12471   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12472 }
12473
12474 void GameActions_RND()
12475 {
12476   int magic_wall_x = 0, magic_wall_y = 0;
12477   int i, x, y, element, graphic;
12478
12479   InitPlayfieldScanModeVars();
12480
12481 #if USE_ONE_MORE_CHANGE_PER_FRAME
12482   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12483   {
12484     SCAN_PLAYFIELD(x, y)
12485     {
12486       ChangeCount[x][y] = 0;
12487       ChangeEvent[x][y] = -1;
12488     }
12489   }
12490 #endif
12491
12492   if (game.set_centered_player)
12493   {
12494     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12495
12496     /* switching to "all players" only possible if all players fit to screen */
12497     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12498     {
12499       game.centered_player_nr_next = game.centered_player_nr;
12500       game.set_centered_player = FALSE;
12501     }
12502
12503     /* do not switch focus to non-existing (or non-active) player */
12504     if (game.centered_player_nr_next >= 0 &&
12505         !stored_player[game.centered_player_nr_next].active)
12506     {
12507       game.centered_player_nr_next = game.centered_player_nr;
12508       game.set_centered_player = FALSE;
12509     }
12510   }
12511
12512   if (game.set_centered_player &&
12513       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12514   {
12515     int sx, sy;
12516
12517     if (game.centered_player_nr_next == -1)
12518     {
12519       setScreenCenteredToAllPlayers(&sx, &sy);
12520     }
12521     else
12522     {
12523       sx = stored_player[game.centered_player_nr_next].jx;
12524       sy = stored_player[game.centered_player_nr_next].jy;
12525     }
12526
12527     game.centered_player_nr = game.centered_player_nr_next;
12528     game.set_centered_player = FALSE;
12529
12530     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12531     DrawGameDoorValues();
12532   }
12533
12534   for (i = 0; i < MAX_PLAYERS; i++)
12535   {
12536     int actual_player_action = stored_player[i].effective_action;
12537
12538 #if 1
12539     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12540        - rnd_equinox_tetrachloride 048
12541        - rnd_equinox_tetrachloride_ii 096
12542        - rnd_emanuel_schmieg 002
12543        - doctor_sloan_ww 001, 020
12544     */
12545     if (stored_player[i].MovPos == 0)
12546       CheckGravityMovement(&stored_player[i]);
12547 #endif
12548
12549     /* overwrite programmed action with tape action */
12550     if (stored_player[i].programmed_action)
12551       actual_player_action = stored_player[i].programmed_action;
12552
12553     PlayerActions(&stored_player[i], actual_player_action);
12554
12555     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12556   }
12557
12558   ScrollScreen(NULL, SCROLL_GO_ON);
12559
12560   /* for backwards compatibility, the following code emulates a fixed bug that
12561      occured when pushing elements (causing elements that just made their last
12562      pushing step to already (if possible) make their first falling step in the
12563      same game frame, which is bad); this code is also needed to use the famous
12564      "spring push bug" which is used in older levels and might be wanted to be
12565      used also in newer levels, but in this case the buggy pushing code is only
12566      affecting the "spring" element and no other elements */
12567
12568   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12569   {
12570     for (i = 0; i < MAX_PLAYERS; i++)
12571     {
12572       struct PlayerInfo *player = &stored_player[i];
12573       int x = player->jx;
12574       int y = player->jy;
12575
12576       if (player->active && player->is_pushing && player->is_moving &&
12577           IS_MOVING(x, y) &&
12578           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12579            Feld[x][y] == EL_SPRING))
12580       {
12581         ContinueMoving(x, y);
12582
12583         /* continue moving after pushing (this is actually a bug) */
12584         if (!IS_MOVING(x, y))
12585           Stop[x][y] = FALSE;
12586       }
12587     }
12588   }
12589
12590 #if 0
12591   debug_print_timestamp(0, "start main loop profiling");
12592 #endif
12593
12594   SCAN_PLAYFIELD(x, y)
12595   {
12596     ChangeCount[x][y] = 0;
12597     ChangeEvent[x][y] = -1;
12598
12599     /* this must be handled before main playfield loop */
12600     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12601     {
12602       MovDelay[x][y]--;
12603       if (MovDelay[x][y] <= 0)
12604         RemoveField(x, y);
12605     }
12606
12607 #if USE_NEW_SNAP_DELAY
12608     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12609     {
12610       MovDelay[x][y]--;
12611       if (MovDelay[x][y] <= 0)
12612       {
12613         RemoveField(x, y);
12614         TEST_DrawLevelField(x, y);
12615
12616         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12617       }
12618     }
12619 #endif
12620
12621 #if DEBUG
12622     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12623     {
12624       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12625       printf("GameActions(): This should never happen!\n");
12626
12627       ChangePage[x][y] = -1;
12628     }
12629 #endif
12630
12631     Stop[x][y] = FALSE;
12632     if (WasJustMoving[x][y] > 0)
12633       WasJustMoving[x][y]--;
12634     if (WasJustFalling[x][y] > 0)
12635       WasJustFalling[x][y]--;
12636     if (CheckCollision[x][y] > 0)
12637       CheckCollision[x][y]--;
12638     if (CheckImpact[x][y] > 0)
12639       CheckImpact[x][y]--;
12640
12641     GfxFrame[x][y]++;
12642
12643     /* reset finished pushing action (not done in ContinueMoving() to allow
12644        continuous pushing animation for elements with zero push delay) */
12645     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12646     {
12647       ResetGfxAnimation(x, y);
12648       TEST_DrawLevelField(x, y);
12649     }
12650
12651 #if DEBUG
12652     if (IS_BLOCKED(x, y))
12653     {
12654       int oldx, oldy;
12655
12656       Blocked2Moving(x, y, &oldx, &oldy);
12657       if (!IS_MOVING(oldx, oldy))
12658       {
12659         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12660         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12661         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12662         printf("GameActions(): This should never happen!\n");
12663       }
12664     }
12665 #endif
12666   }
12667
12668 #if 0
12669   debug_print_timestamp(0, "- time for pre-main loop:");
12670 #endif
12671
12672 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12673   SCAN_PLAYFIELD(x, y)
12674   {
12675     element = Feld[x][y];
12676     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12677
12678 #if 1
12679     {
12680 #if 1
12681       int element2 = element;
12682       int graphic2 = graphic;
12683 #else
12684       int element2 = Feld[x][y];
12685       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12686 #endif
12687       int last_gfx_frame = GfxFrame[x][y];
12688
12689       if (graphic_info[graphic2].anim_global_sync)
12690         GfxFrame[x][y] = FrameCounter;
12691       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12692         GfxFrame[x][y] = CustomValue[x][y];
12693       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12694         GfxFrame[x][y] = element_info[element2].collect_score;
12695       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12696         GfxFrame[x][y] = ChangeDelay[x][y];
12697
12698       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12699         DrawLevelGraphicAnimation(x, y, graphic2);
12700     }
12701 #else
12702     ResetGfxFrame(x, y, TRUE);
12703 #endif
12704
12705 #if 1
12706     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12707         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12708       ResetRandomAnimationValue(x, y);
12709 #endif
12710
12711 #if 1
12712     SetRandomAnimationValue(x, y);
12713 #endif
12714
12715 #if 1
12716     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12717 #endif
12718   }
12719 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12720
12721 #if 0
12722   debug_print_timestamp(0, "- time for TEST loop:     -->");
12723 #endif
12724
12725   SCAN_PLAYFIELD(x, y)
12726   {
12727     element = Feld[x][y];
12728     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12729
12730     ResetGfxFrame(x, y, TRUE);
12731
12732     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12733         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12734       ResetRandomAnimationValue(x, y);
12735
12736     SetRandomAnimationValue(x, y);
12737
12738     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12739
12740     if (IS_INACTIVE(element))
12741     {
12742       if (IS_ANIMATED(graphic))
12743         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12744
12745       continue;
12746     }
12747
12748     /* this may take place after moving, so 'element' may have changed */
12749     if (IS_CHANGING(x, y) &&
12750         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12751     {
12752       int page = element_info[element].event_page_nr[CE_DELAY];
12753
12754 #if 1
12755       HandleElementChange(x, y, page);
12756 #else
12757       if (CAN_CHANGE(element))
12758         HandleElementChange(x, y, page);
12759
12760       if (HAS_ACTION(element))
12761         ExecuteCustomElementAction(x, y, element, page);
12762 #endif
12763
12764       element = Feld[x][y];
12765       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12766     }
12767
12768 #if 0   // ---------------------------------------------------------------------
12769
12770     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12771     {
12772       StartMoving(x, y);
12773
12774       element = Feld[x][y];
12775       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12776
12777       if (IS_ANIMATED(graphic) &&
12778           !IS_MOVING(x, y) &&
12779           !Stop[x][y])
12780         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12781
12782       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12783         TEST_DrawTwinkleOnField(x, y);
12784     }
12785     else if (IS_MOVING(x, y))
12786       ContinueMoving(x, y);
12787     else
12788     {
12789       switch (element)
12790       {
12791         case EL_ACID:
12792         case EL_EXIT_OPEN:
12793         case EL_EM_EXIT_OPEN:
12794         case EL_SP_EXIT_OPEN:
12795         case EL_STEEL_EXIT_OPEN:
12796         case EL_EM_STEEL_EXIT_OPEN:
12797         case EL_SP_TERMINAL:
12798         case EL_SP_TERMINAL_ACTIVE:
12799         case EL_EXTRA_TIME:
12800         case EL_SHIELD_NORMAL:
12801         case EL_SHIELD_DEADLY:
12802           if (IS_ANIMATED(graphic))
12803             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12804           break;
12805
12806         case EL_DYNAMITE_ACTIVE:
12807         case EL_EM_DYNAMITE_ACTIVE:
12808         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12809         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12810         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12811         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12812         case EL_SP_DISK_RED_ACTIVE:
12813           CheckDynamite(x, y);
12814           break;
12815
12816         case EL_AMOEBA_GROWING:
12817           AmoebeWaechst(x, y);
12818           break;
12819
12820         case EL_AMOEBA_SHRINKING:
12821           AmoebaDisappearing(x, y);
12822           break;
12823
12824 #if !USE_NEW_AMOEBA_CODE
12825         case EL_AMOEBA_WET:
12826         case EL_AMOEBA_DRY:
12827         case EL_AMOEBA_FULL:
12828         case EL_BD_AMOEBA:
12829         case EL_EMC_DRIPPER:
12830           AmoebeAbleger(x, y);
12831           break;
12832 #endif
12833
12834         case EL_GAME_OF_LIFE:
12835         case EL_BIOMAZE:
12836           Life(x, y);
12837           break;
12838
12839         case EL_EXIT_CLOSED:
12840           CheckExit(x, y);
12841           break;
12842
12843         case EL_EM_EXIT_CLOSED:
12844           CheckExitEM(x, y);
12845           break;
12846
12847         case EL_STEEL_EXIT_CLOSED:
12848           CheckExitSteel(x, y);
12849           break;
12850
12851         case EL_EM_STEEL_EXIT_CLOSED:
12852           CheckExitSteelEM(x, y);
12853           break;
12854
12855         case EL_SP_EXIT_CLOSED:
12856           CheckExitSP(x, y);
12857           break;
12858
12859         case EL_EXPANDABLE_WALL_GROWING:
12860         case EL_EXPANDABLE_STEELWALL_GROWING:
12861           MauerWaechst(x, y);
12862           break;
12863
12864         case EL_EXPANDABLE_WALL:
12865         case EL_EXPANDABLE_WALL_HORIZONTAL:
12866         case EL_EXPANDABLE_WALL_VERTICAL:
12867         case EL_EXPANDABLE_WALL_ANY:
12868         case EL_BD_EXPANDABLE_WALL:
12869           MauerAbleger(x, y);
12870           break;
12871
12872         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12873         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12874         case EL_EXPANDABLE_STEELWALL_ANY:
12875           MauerAblegerStahl(x, y);
12876           break;
12877
12878         case EL_FLAMES:
12879           CheckForDragon(x, y);
12880           break;
12881
12882         case EL_EXPLOSION:
12883           break;
12884
12885         case EL_ELEMENT_SNAPPING:
12886         case EL_DIAGONAL_SHRINKING:
12887         case EL_DIAGONAL_GROWING:
12888         {
12889           graphic =
12890             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12891
12892           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12893           break;
12894         }
12895
12896         default:
12897           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12898             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12899           break;
12900       }
12901     }
12902
12903 #else   // ---------------------------------------------------------------------
12904
12905     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12906     {
12907       StartMoving(x, y);
12908
12909       element = Feld[x][y];
12910       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12911
12912       if (IS_ANIMATED(graphic) &&
12913           !IS_MOVING(x, y) &&
12914           !Stop[x][y])
12915         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12916
12917       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12918         TEST_DrawTwinkleOnField(x, y);
12919     }
12920     else if ((element == EL_ACID ||
12921               element == EL_EXIT_OPEN ||
12922               element == EL_EM_EXIT_OPEN ||
12923               element == EL_SP_EXIT_OPEN ||
12924               element == EL_STEEL_EXIT_OPEN ||
12925               element == EL_EM_STEEL_EXIT_OPEN ||
12926               element == EL_SP_TERMINAL ||
12927               element == EL_SP_TERMINAL_ACTIVE ||
12928               element == EL_EXTRA_TIME ||
12929               element == EL_SHIELD_NORMAL ||
12930               element == EL_SHIELD_DEADLY) &&
12931              IS_ANIMATED(graphic))
12932       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12933     else if (IS_MOVING(x, y))
12934       ContinueMoving(x, y);
12935     else if (IS_ACTIVE_BOMB(element))
12936       CheckDynamite(x, y);
12937     else if (element == EL_AMOEBA_GROWING)
12938       AmoebeWaechst(x, y);
12939     else if (element == EL_AMOEBA_SHRINKING)
12940       AmoebaDisappearing(x, y);
12941
12942 #if !USE_NEW_AMOEBA_CODE
12943     else if (IS_AMOEBALIVE(element))
12944       AmoebeAbleger(x, y);
12945 #endif
12946
12947     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12948       Life(x, y);
12949     else if (element == EL_EXIT_CLOSED)
12950       CheckExit(x, y);
12951     else if (element == EL_EM_EXIT_CLOSED)
12952       CheckExitEM(x, y);
12953     else if (element == EL_STEEL_EXIT_CLOSED)
12954       CheckExitSteel(x, y);
12955     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12956       CheckExitSteelEM(x, y);
12957     else if (element == EL_SP_EXIT_CLOSED)
12958       CheckExitSP(x, y);
12959     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12960              element == EL_EXPANDABLE_STEELWALL_GROWING)
12961       MauerWaechst(x, y);
12962     else if (element == EL_EXPANDABLE_WALL ||
12963              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12964              element == EL_EXPANDABLE_WALL_VERTICAL ||
12965              element == EL_EXPANDABLE_WALL_ANY ||
12966              element == EL_BD_EXPANDABLE_WALL)
12967       MauerAbleger(x, y);
12968     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12969              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12970              element == EL_EXPANDABLE_STEELWALL_ANY)
12971       MauerAblegerStahl(x, y);
12972     else if (element == EL_FLAMES)
12973       CheckForDragon(x, y);
12974     else if (element == EL_EXPLOSION)
12975       ; /* drawing of correct explosion animation is handled separately */
12976     else if (element == EL_ELEMENT_SNAPPING ||
12977              element == EL_DIAGONAL_SHRINKING ||
12978              element == EL_DIAGONAL_GROWING)
12979     {
12980       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12981
12982       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12983     }
12984     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12985       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12986
12987 #endif  // ---------------------------------------------------------------------
12988
12989     if (IS_BELT_ACTIVE(element))
12990       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12991
12992     if (game.magic_wall_active)
12993     {
12994       int jx = local_player->jx, jy = local_player->jy;
12995
12996       /* play the element sound at the position nearest to the player */
12997       if ((element == EL_MAGIC_WALL_FULL ||
12998            element == EL_MAGIC_WALL_ACTIVE ||
12999            element == EL_MAGIC_WALL_EMPTYING ||
13000            element == EL_BD_MAGIC_WALL_FULL ||
13001            element == EL_BD_MAGIC_WALL_ACTIVE ||
13002            element == EL_BD_MAGIC_WALL_EMPTYING ||
13003            element == EL_DC_MAGIC_WALL_FULL ||
13004            element == EL_DC_MAGIC_WALL_ACTIVE ||
13005            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13006           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13007       {
13008         magic_wall_x = x;
13009         magic_wall_y = y;
13010       }
13011     }
13012   }
13013
13014 #if 0
13015   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13016 #endif
13017
13018 #if USE_NEW_AMOEBA_CODE
13019   /* new experimental amoeba growth stuff */
13020   if (!(FrameCounter % 8))
13021   {
13022     static unsigned long random = 1684108901;
13023
13024     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13025     {
13026       x = RND(lev_fieldx);
13027       y = RND(lev_fieldy);
13028       element = Feld[x][y];
13029
13030       if (!IS_PLAYER(x,y) &&
13031           (element == EL_EMPTY ||
13032            CAN_GROW_INTO(element) ||
13033            element == EL_QUICKSAND_EMPTY ||
13034            element == EL_QUICKSAND_FAST_EMPTY ||
13035            element == EL_ACID_SPLASH_LEFT ||
13036            element == EL_ACID_SPLASH_RIGHT))
13037       {
13038         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13039             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13040             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13041             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13042           Feld[x][y] = EL_AMOEBA_DROP;
13043       }
13044
13045       random = random * 129 + 1;
13046     }
13047   }
13048 #endif
13049
13050 #if 0
13051   if (game.explosions_delayed)
13052 #endif
13053   {
13054     game.explosions_delayed = FALSE;
13055
13056     SCAN_PLAYFIELD(x, y)
13057     {
13058       element = Feld[x][y];
13059
13060       if (ExplodeField[x][y])
13061         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13062       else if (element == EL_EXPLOSION)
13063         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13064
13065       ExplodeField[x][y] = EX_TYPE_NONE;
13066     }
13067
13068     game.explosions_delayed = TRUE;
13069   }
13070
13071   if (game.magic_wall_active)
13072   {
13073     if (!(game.magic_wall_time_left % 4))
13074     {
13075       int element = Feld[magic_wall_x][magic_wall_y];
13076
13077       if (element == EL_BD_MAGIC_WALL_FULL ||
13078           element == EL_BD_MAGIC_WALL_ACTIVE ||
13079           element == EL_BD_MAGIC_WALL_EMPTYING)
13080         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13081       else if (element == EL_DC_MAGIC_WALL_FULL ||
13082                element == EL_DC_MAGIC_WALL_ACTIVE ||
13083                element == EL_DC_MAGIC_WALL_EMPTYING)
13084         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13085       else
13086         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13087     }
13088
13089     if (game.magic_wall_time_left > 0)
13090     {
13091       game.magic_wall_time_left--;
13092
13093       if (!game.magic_wall_time_left)
13094       {
13095         SCAN_PLAYFIELD(x, y)
13096         {
13097           element = Feld[x][y];
13098
13099           if (element == EL_MAGIC_WALL_ACTIVE ||
13100               element == EL_MAGIC_WALL_FULL)
13101           {
13102             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13103             TEST_DrawLevelField(x, y);
13104           }
13105           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13106                    element == EL_BD_MAGIC_WALL_FULL)
13107           {
13108             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13109             TEST_DrawLevelField(x, y);
13110           }
13111           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13112                    element == EL_DC_MAGIC_WALL_FULL)
13113           {
13114             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13115             TEST_DrawLevelField(x, y);
13116           }
13117         }
13118
13119         game.magic_wall_active = FALSE;
13120       }
13121     }
13122   }
13123
13124   if (game.light_time_left > 0)
13125   {
13126     game.light_time_left--;
13127
13128     if (game.light_time_left == 0)
13129       RedrawAllLightSwitchesAndInvisibleElements();
13130   }
13131
13132   if (game.timegate_time_left > 0)
13133   {
13134     game.timegate_time_left--;
13135
13136     if (game.timegate_time_left == 0)
13137       CloseAllOpenTimegates();
13138   }
13139
13140   if (game.lenses_time_left > 0)
13141   {
13142     game.lenses_time_left--;
13143
13144     if (game.lenses_time_left == 0)
13145       RedrawAllInvisibleElementsForLenses();
13146   }
13147
13148   if (game.magnify_time_left > 0)
13149   {
13150     game.magnify_time_left--;
13151
13152     if (game.magnify_time_left == 0)
13153       RedrawAllInvisibleElementsForMagnifier();
13154   }
13155
13156   for (i = 0; i < MAX_PLAYERS; i++)
13157   {
13158     struct PlayerInfo *player = &stored_player[i];
13159
13160     if (SHIELD_ON(player))
13161     {
13162       if (player->shield_deadly_time_left)
13163         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13164       else if (player->shield_normal_time_left)
13165         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13166     }
13167   }
13168
13169 #if USE_DELAYED_GFX_REDRAW
13170   SCAN_PLAYFIELD(x, y)
13171   {
13172 #if 1
13173     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13174 #else
13175     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13176         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13177 #endif
13178     {
13179       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13180          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13181
13182       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13183         DrawLevelField(x, y);
13184
13185       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13186         DrawLevelFieldCrumbled(x, y);
13187
13188       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13189         DrawLevelFieldCrumbledNeighbours(x, y);
13190
13191       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13192         DrawTwinkleOnField(x, y);
13193     }
13194
13195     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13196   }
13197 #endif
13198
13199   CheckLevelTime();
13200
13201   DrawAllPlayers();
13202   PlayAllPlayersSound();
13203
13204   if (options.debug)                    /* calculate frames per second */
13205   {
13206     static unsigned long fps_counter = 0;
13207     static int fps_frames = 0;
13208     unsigned long fps_delay_ms = Counter() - fps_counter;
13209
13210     fps_frames++;
13211
13212     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13213     {
13214       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13215
13216       fps_frames = 0;
13217       fps_counter = Counter();
13218     }
13219
13220     redraw_mask |= REDRAW_FPS;
13221   }
13222
13223   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13224
13225   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13226   {
13227     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13228
13229     local_player->show_envelope = 0;
13230   }
13231
13232 #if 0
13233   debug_print_timestamp(0, "stop main loop profiling ");
13234   printf("----------------------------------------------------------\n");
13235 #endif
13236
13237   /* use random number generator in every frame to make it less predictable */
13238   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13239     RND(1);
13240 }
13241
13242 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13243 {
13244   int min_x = x, min_y = y, max_x = x, max_y = y;
13245   int i;
13246
13247   for (i = 0; i < MAX_PLAYERS; i++)
13248   {
13249     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13250
13251     if (!stored_player[i].active || &stored_player[i] == player)
13252       continue;
13253
13254     min_x = MIN(min_x, jx);
13255     min_y = MIN(min_y, jy);
13256     max_x = MAX(max_x, jx);
13257     max_y = MAX(max_y, jy);
13258   }
13259
13260   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13261 }
13262
13263 static boolean AllPlayersInVisibleScreen()
13264 {
13265   int i;
13266
13267   for (i = 0; i < MAX_PLAYERS; i++)
13268   {
13269     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13270
13271     if (!stored_player[i].active)
13272       continue;
13273
13274     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13275       return FALSE;
13276   }
13277
13278   return TRUE;
13279 }
13280
13281 void ScrollLevel(int dx, int dy)
13282 {
13283 #if 0
13284   /* (directly solved in BlitBitmap() now) */
13285   static Bitmap *bitmap_db_field2 = NULL;
13286   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13287   int x, y;
13288 #else
13289   int x, y;
13290 #endif
13291
13292 #if 0
13293   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13294   /* only horizontal XOR vertical scroll direction allowed */
13295   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13296     return;
13297 #endif
13298
13299 #if 0
13300   /* (directly solved in BlitBitmap() now) */
13301   if (bitmap_db_field2 == NULL)
13302     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13303
13304   /* needed when blitting directly to same bitmap -- should not be needed with
13305      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13306   BlitBitmap(drawto_field, bitmap_db_field2,
13307              FX + TILEX * (dx == -1) - softscroll_offset,
13308              FY + TILEY * (dy == -1) - softscroll_offset,
13309              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13310              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13311              FX + TILEX * (dx == 1) - softscroll_offset,
13312              FY + TILEY * (dy == 1) - softscroll_offset);
13313   BlitBitmap(bitmap_db_field2, drawto_field,
13314              FX + TILEX * (dx == 1) - softscroll_offset,
13315              FY + TILEY * (dy == 1) - softscroll_offset,
13316              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13317              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13318              FX + TILEX * (dx == 1) - softscroll_offset,
13319              FY + TILEY * (dy == 1) - softscroll_offset);
13320
13321 #else
13322
13323 #if 0
13324   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13325   int xsize = (BX2 - BX1 + 1);
13326   int ysize = (BY2 - BY1 + 1);
13327   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13328   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13329   int step  = (start < end ? +1 : -1);
13330
13331   for (i = start; i != end; i += step)
13332   {
13333     BlitBitmap(drawto_field, drawto_field,
13334                FX + TILEX * (dx != 0 ? i + step : 0),
13335                FY + TILEY * (dy != 0 ? i + step : 0),
13336                TILEX * (dx != 0 ? 1 : xsize),
13337                TILEY * (dy != 0 ? 1 : ysize),
13338                FX + TILEX * (dx != 0 ? i : 0),
13339                FY + TILEY * (dy != 0 ? i : 0));
13340   }
13341
13342 #else
13343
13344   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13345
13346   BlitBitmap(drawto_field, drawto_field,
13347              FX + TILEX * (dx == -1) - softscroll_offset,
13348              FY + TILEY * (dy == -1) - softscroll_offset,
13349              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13350              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13351              FX + TILEX * (dx == 1) - softscroll_offset,
13352              FY + TILEY * (dy == 1) - softscroll_offset);
13353 #endif
13354 #endif
13355
13356   if (dx != 0)
13357   {
13358     x = (dx == 1 ? BX1 : BX2);
13359     for (y = BY1; y <= BY2; y++)
13360       DrawScreenField(x, y);
13361   }
13362
13363   if (dy != 0)
13364   {
13365     y = (dy == 1 ? BY1 : BY2);
13366     for (x = BX1; x <= BX2; x++)
13367       DrawScreenField(x, y);
13368   }
13369
13370   redraw_mask |= REDRAW_FIELD;
13371 }
13372
13373 static boolean canFallDown(struct PlayerInfo *player)
13374 {
13375   int jx = player->jx, jy = player->jy;
13376
13377   return (IN_LEV_FIELD(jx, jy + 1) &&
13378           (IS_FREE(jx, jy + 1) ||
13379            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13380           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13381           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13382 }
13383
13384 static boolean canPassField(int x, int y, int move_dir)
13385 {
13386   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13387   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13388   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13389   int nextx = x + dx;
13390   int nexty = y + dy;
13391   int element = Feld[x][y];
13392
13393   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13394           !CAN_MOVE(element) &&
13395           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13396           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13397           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13398 }
13399
13400 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13401 {
13402   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13403   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13404   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13405   int newx = x + dx;
13406   int newy = y + dy;
13407
13408   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13409           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13410           (IS_DIGGABLE(Feld[newx][newy]) ||
13411            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13412            canPassField(newx, newy, move_dir)));
13413 }
13414
13415 static void CheckGravityMovement(struct PlayerInfo *player)
13416 {
13417 #if USE_PLAYER_GRAVITY
13418   if (player->gravity && !player->programmed_action)
13419 #else
13420   if (game.gravity && !player->programmed_action)
13421 #endif
13422   {
13423     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13424     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13425     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13426     int jx = player->jx, jy = player->jy;
13427     boolean player_is_moving_to_valid_field =
13428       (!player_is_snapping &&
13429        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13430         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13431     boolean player_can_fall_down = canFallDown(player);
13432
13433     if (player_can_fall_down &&
13434         !player_is_moving_to_valid_field)
13435       player->programmed_action = MV_DOWN;
13436   }
13437 }
13438
13439 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13440 {
13441   return CheckGravityMovement(player);
13442
13443 #if USE_PLAYER_GRAVITY
13444   if (player->gravity && !player->programmed_action)
13445 #else
13446   if (game.gravity && !player->programmed_action)
13447 #endif
13448   {
13449     int jx = player->jx, jy = player->jy;
13450     boolean field_under_player_is_free =
13451       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13452     boolean player_is_standing_on_valid_field =
13453       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13454        (IS_WALKABLE(Feld[jx][jy]) &&
13455         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13456
13457     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13458       player->programmed_action = MV_DOWN;
13459   }
13460 }
13461
13462 /*
13463   MovePlayerOneStep()
13464   -----------------------------------------------------------------------------
13465   dx, dy:               direction (non-diagonal) to try to move the player to
13466   real_dx, real_dy:     direction as read from input device (can be diagonal)
13467 */
13468
13469 boolean MovePlayerOneStep(struct PlayerInfo *player,
13470                           int dx, int dy, int real_dx, int real_dy)
13471 {
13472   int jx = player->jx, jy = player->jy;
13473   int new_jx = jx + dx, new_jy = jy + dy;
13474 #if !USE_FIXED_DONT_RUN_INTO
13475   int element;
13476 #endif
13477   int can_move;
13478   boolean player_can_move = !player->cannot_move;
13479
13480   if (!player->active || (!dx && !dy))
13481     return MP_NO_ACTION;
13482
13483   player->MovDir = (dx < 0 ? MV_LEFT :
13484                     dx > 0 ? MV_RIGHT :
13485                     dy < 0 ? MV_UP :
13486                     dy > 0 ? MV_DOWN :  MV_NONE);
13487
13488   if (!IN_LEV_FIELD(new_jx, new_jy))
13489     return MP_NO_ACTION;
13490
13491   if (!player_can_move)
13492   {
13493     if (player->MovPos == 0)
13494     {
13495       player->is_moving = FALSE;
13496       player->is_digging = FALSE;
13497       player->is_collecting = FALSE;
13498       player->is_snapping = FALSE;
13499       player->is_pushing = FALSE;
13500     }
13501   }
13502
13503 #if 1
13504   if (!options.network && game.centered_player_nr == -1 &&
13505       !AllPlayersInSight(player, new_jx, new_jy))
13506     return MP_NO_ACTION;
13507 #else
13508   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13509     return MP_NO_ACTION;
13510 #endif
13511
13512 #if !USE_FIXED_DONT_RUN_INTO
13513   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13514
13515   /* (moved to DigField()) */
13516   if (player_can_move && DONT_RUN_INTO(element))
13517   {
13518     if (element == EL_ACID && dx == 0 && dy == 1)
13519     {
13520       SplashAcid(new_jx, new_jy);
13521       Feld[jx][jy] = EL_PLAYER_1;
13522       InitMovingField(jx, jy, MV_DOWN);
13523       Store[jx][jy] = EL_ACID;
13524       ContinueMoving(jx, jy);
13525       BuryPlayer(player);
13526     }
13527     else
13528       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13529
13530     return MP_MOVING;
13531   }
13532 #endif
13533
13534   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13535   if (can_move != MP_MOVING)
13536     return can_move;
13537
13538   /* check if DigField() has caused relocation of the player */
13539   if (player->jx != jx || player->jy != jy)
13540     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13541
13542   StorePlayer[jx][jy] = 0;
13543   player->last_jx = jx;
13544   player->last_jy = jy;
13545   player->jx = new_jx;
13546   player->jy = new_jy;
13547   StorePlayer[new_jx][new_jy] = player->element_nr;
13548
13549   if (player->move_delay_value_next != -1)
13550   {
13551     player->move_delay_value = player->move_delay_value_next;
13552     player->move_delay_value_next = -1;
13553   }
13554
13555   player->MovPos =
13556     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13557
13558   player->step_counter++;
13559
13560   PlayerVisit[jx][jy] = FrameCounter;
13561
13562 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13563   player->is_moving = TRUE;
13564 #endif
13565
13566 #if 1
13567   /* should better be called in MovePlayer(), but this breaks some tapes */
13568   ScrollPlayer(player, SCROLL_INIT);
13569 #endif
13570
13571   return MP_MOVING;
13572 }
13573
13574 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13575 {
13576   int jx = player->jx, jy = player->jy;
13577   int old_jx = jx, old_jy = jy;
13578   int moved = MP_NO_ACTION;
13579
13580   if (!player->active)
13581     return FALSE;
13582
13583   if (!dx && !dy)
13584   {
13585     if (player->MovPos == 0)
13586     {
13587       player->is_moving = FALSE;
13588       player->is_digging = FALSE;
13589       player->is_collecting = FALSE;
13590       player->is_snapping = FALSE;
13591       player->is_pushing = FALSE;
13592     }
13593
13594     return FALSE;
13595   }
13596
13597   if (player->move_delay > 0)
13598     return FALSE;
13599
13600   player->move_delay = -1;              /* set to "uninitialized" value */
13601
13602   /* store if player is automatically moved to next field */
13603   player->is_auto_moving = (player->programmed_action != MV_NONE);
13604
13605   /* remove the last programmed player action */
13606   player->programmed_action = 0;
13607
13608   if (player->MovPos)
13609   {
13610     /* should only happen if pre-1.2 tape recordings are played */
13611     /* this is only for backward compatibility */
13612
13613     int original_move_delay_value = player->move_delay_value;
13614
13615 #if DEBUG
13616     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13617            tape.counter);
13618 #endif
13619
13620     /* scroll remaining steps with finest movement resolution */
13621     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13622
13623     while (player->MovPos)
13624     {
13625       ScrollPlayer(player, SCROLL_GO_ON);
13626       ScrollScreen(NULL, SCROLL_GO_ON);
13627
13628       AdvanceFrameAndPlayerCounters(player->index_nr);
13629
13630       DrawAllPlayers();
13631       BackToFront();
13632     }
13633
13634     player->move_delay_value = original_move_delay_value;
13635   }
13636
13637   player->is_active = FALSE;
13638
13639   if (player->last_move_dir & MV_HORIZONTAL)
13640   {
13641     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13642       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13643   }
13644   else
13645   {
13646     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13647       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13648   }
13649
13650 #if USE_FIXED_BORDER_RUNNING_GFX
13651   if (!moved && !player->is_active)
13652   {
13653     player->is_moving = FALSE;
13654     player->is_digging = FALSE;
13655     player->is_collecting = FALSE;
13656     player->is_snapping = FALSE;
13657     player->is_pushing = FALSE;
13658   }
13659 #endif
13660
13661   jx = player->jx;
13662   jy = player->jy;
13663
13664 #if 1
13665   if (moved & MP_MOVING && !ScreenMovPos &&
13666       (player->index_nr == game.centered_player_nr ||
13667        game.centered_player_nr == -1))
13668 #else
13669   if (moved & MP_MOVING && !ScreenMovPos &&
13670       (player == local_player || !options.network))
13671 #endif
13672   {
13673     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13674     int offset = game.scroll_delay_value;
13675
13676     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13677     {
13678       /* actual player has left the screen -- scroll in that direction */
13679       if (jx != old_jx)         /* player has moved horizontally */
13680         scroll_x += (jx - old_jx);
13681       else                      /* player has moved vertically */
13682         scroll_y += (jy - old_jy);
13683     }
13684     else
13685     {
13686       if (jx != old_jx)         /* player has moved horizontally */
13687       {
13688         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13689             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13690           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13691
13692         /* don't scroll over playfield boundaries */
13693         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13694           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13695
13696         /* don't scroll more than one field at a time */
13697         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13698
13699         /* don't scroll against the player's moving direction */
13700         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13701             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13702           scroll_x = old_scroll_x;
13703       }
13704       else                      /* player has moved vertically */
13705       {
13706         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13707             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13708           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13709
13710         /* don't scroll over playfield boundaries */
13711         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13712           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13713
13714         /* don't scroll more than one field at a time */
13715         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13716
13717         /* don't scroll against the player's moving direction */
13718         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13719             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13720           scroll_y = old_scroll_y;
13721       }
13722     }
13723
13724     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13725     {
13726 #if 1
13727       if (!options.network && game.centered_player_nr == -1 &&
13728           !AllPlayersInVisibleScreen())
13729       {
13730         scroll_x = old_scroll_x;
13731         scroll_y = old_scroll_y;
13732       }
13733       else
13734 #else
13735       if (!options.network && !AllPlayersInVisibleScreen())
13736       {
13737         scroll_x = old_scroll_x;
13738         scroll_y = old_scroll_y;
13739       }
13740       else
13741 #endif
13742       {
13743         ScrollScreen(player, SCROLL_INIT);
13744         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13745       }
13746     }
13747   }
13748
13749   player->StepFrame = 0;
13750
13751   if (moved & MP_MOVING)
13752   {
13753     if (old_jx != jx && old_jy == jy)
13754       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13755     else if (old_jx == jx && old_jy != jy)
13756       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13757
13758     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13759
13760     player->last_move_dir = player->MovDir;
13761     player->is_moving = TRUE;
13762     player->is_snapping = FALSE;
13763     player->is_switching = FALSE;
13764     player->is_dropping = FALSE;
13765     player->is_dropping_pressed = FALSE;
13766     player->drop_pressed_delay = 0;
13767
13768 #if 0
13769     /* should better be called here than above, but this breaks some tapes */
13770     ScrollPlayer(player, SCROLL_INIT);
13771 #endif
13772   }
13773   else
13774   {
13775     CheckGravityMovementWhenNotMoving(player);
13776
13777     player->is_moving = FALSE;
13778
13779     /* at this point, the player is allowed to move, but cannot move right now
13780        (e.g. because of something blocking the way) -- ensure that the player
13781        is also allowed to move in the next frame (in old versions before 3.1.1,
13782        the player was forced to wait again for eight frames before next try) */
13783
13784     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13785       player->move_delay = 0;   /* allow direct movement in the next frame */
13786   }
13787
13788   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13789     player->move_delay = player->move_delay_value;
13790
13791   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13792   {
13793     TestIfPlayerTouchesBadThing(jx, jy);
13794     TestIfPlayerTouchesCustomElement(jx, jy);
13795   }
13796
13797   if (!player->active)
13798     RemovePlayer(player);
13799
13800   return moved;
13801 }
13802
13803 void ScrollPlayer(struct PlayerInfo *player, int mode)
13804 {
13805   int jx = player->jx, jy = player->jy;
13806   int last_jx = player->last_jx, last_jy = player->last_jy;
13807   int move_stepsize = TILEX / player->move_delay_value;
13808
13809 #if USE_NEW_PLAYER_SPEED
13810   if (!player->active)
13811     return;
13812
13813   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13814     return;
13815 #else
13816   if (!player->active || player->MovPos == 0)
13817     return;
13818 #endif
13819
13820   if (mode == SCROLL_INIT)
13821   {
13822     player->actual_frame_counter = FrameCounter;
13823     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13824
13825     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13826         Feld[last_jx][last_jy] == EL_EMPTY)
13827     {
13828       int last_field_block_delay = 0;   /* start with no blocking at all */
13829       int block_delay_adjustment = player->block_delay_adjustment;
13830
13831       /* if player blocks last field, add delay for exactly one move */
13832       if (player->block_last_field)
13833       {
13834         last_field_block_delay += player->move_delay_value;
13835
13836         /* when blocking enabled, prevent moving up despite gravity */
13837 #if USE_PLAYER_GRAVITY
13838         if (player->gravity && player->MovDir == MV_UP)
13839           block_delay_adjustment = -1;
13840 #else
13841         if (game.gravity && player->MovDir == MV_UP)
13842           block_delay_adjustment = -1;
13843 #endif
13844       }
13845
13846       /* add block delay adjustment (also possible when not blocking) */
13847       last_field_block_delay += block_delay_adjustment;
13848
13849       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13850       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13851     }
13852
13853 #if USE_NEW_PLAYER_SPEED
13854     if (player->MovPos != 0)    /* player has not yet reached destination */
13855       return;
13856 #else
13857     return;
13858 #endif
13859   }
13860   else if (!FrameReached(&player->actual_frame_counter, 1))
13861     return;
13862
13863 #if USE_NEW_PLAYER_SPEED
13864   if (player->MovPos != 0)
13865   {
13866     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13867     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13868
13869     /* before DrawPlayer() to draw correct player graphic for this case */
13870     if (player->MovPos == 0)
13871       CheckGravityMovement(player);
13872   }
13873 #else
13874   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13875   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13876
13877   /* before DrawPlayer() to draw correct player graphic for this case */
13878   if (player->MovPos == 0)
13879     CheckGravityMovement(player);
13880 #endif
13881
13882   if (player->MovPos == 0)      /* player reached destination field */
13883   {
13884     if (player->move_delay_reset_counter > 0)
13885     {
13886       player->move_delay_reset_counter--;
13887
13888       if (player->move_delay_reset_counter == 0)
13889       {
13890         /* continue with normal speed after quickly moving through gate */
13891         HALVE_PLAYER_SPEED(player);
13892
13893         /* be able to make the next move without delay */
13894         player->move_delay = 0;
13895       }
13896     }
13897
13898     player->last_jx = jx;
13899     player->last_jy = jy;
13900
13901     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13902         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13903 #if 1
13904         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13905 #endif
13906         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13907         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13908 #if 1
13909         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13910 #endif
13911         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13912         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13913     {
13914       DrawPlayer(player);       /* needed here only to cleanup last field */
13915       RemovePlayer(player);
13916
13917       if (local_player->friends_still_needed == 0 ||
13918           IS_SP_ELEMENT(Feld[jx][jy]))
13919         PlayerWins(player);
13920     }
13921
13922     /* this breaks one level: "machine", level 000 */
13923     {
13924       int move_direction = player->MovDir;
13925       int enter_side = MV_DIR_OPPOSITE(move_direction);
13926       int leave_side = move_direction;
13927       int old_jx = last_jx;
13928       int old_jy = last_jy;
13929       int old_element = Feld[old_jx][old_jy];
13930       int new_element = Feld[jx][jy];
13931
13932       if (IS_CUSTOM_ELEMENT(old_element))
13933         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13934                                    CE_LEFT_BY_PLAYER,
13935                                    player->index_bit, leave_side);
13936
13937       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13938                                           CE_PLAYER_LEAVES_X,
13939                                           player->index_bit, leave_side);
13940
13941       if (IS_CUSTOM_ELEMENT(new_element))
13942         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13943                                    player->index_bit, enter_side);
13944
13945       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13946                                           CE_PLAYER_ENTERS_X,
13947                                           player->index_bit, enter_side);
13948
13949 #if USE_FIX_CE_ACTION_WITH_PLAYER
13950       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13951                                         CE_MOVE_OF_X, move_direction);
13952 #else
13953       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13954                                         CE_MOVE_OF_X, move_direction);
13955 #endif
13956     }
13957
13958     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13959     {
13960       TestIfPlayerTouchesBadThing(jx, jy);
13961       TestIfPlayerTouchesCustomElement(jx, jy);
13962
13963       /* needed because pushed element has not yet reached its destination,
13964          so it would trigger a change event at its previous field location */
13965       if (!player->is_pushing)
13966         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13967
13968       if (!player->active)
13969         RemovePlayer(player);
13970     }
13971
13972     if (!local_player->LevelSolved && level.use_step_counter)
13973     {
13974       int i;
13975
13976       TimePlayed++;
13977
13978       if (TimeLeft > 0)
13979       {
13980         TimeLeft--;
13981
13982         if (TimeLeft <= 10 && setup.time_limit)
13983           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13984
13985 #if 1
13986         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13987
13988         DisplayGameControlValues();
13989 #else
13990         DrawGameValue_Time(TimeLeft);
13991 #endif
13992
13993         if (!TimeLeft && setup.time_limit)
13994           for (i = 0; i < MAX_PLAYERS; i++)
13995             KillPlayer(&stored_player[i]);
13996       }
13997 #if 1
13998       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13999       {
14000         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14001
14002         DisplayGameControlValues();
14003       }
14004 #else
14005       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14006         DrawGameValue_Time(TimePlayed);
14007 #endif
14008     }
14009
14010     if (tape.single_step && tape.recording && !tape.pausing &&
14011         !player->programmed_action)
14012       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14013   }
14014 }
14015
14016 void ScrollScreen(struct PlayerInfo *player, int mode)
14017 {
14018   static unsigned long screen_frame_counter = 0;
14019
14020   if (mode == SCROLL_INIT)
14021   {
14022     /* set scrolling step size according to actual player's moving speed */
14023     ScrollStepSize = TILEX / player->move_delay_value;
14024
14025     screen_frame_counter = FrameCounter;
14026     ScreenMovDir = player->MovDir;
14027     ScreenMovPos = player->MovPos;
14028     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14029     return;
14030   }
14031   else if (!FrameReached(&screen_frame_counter, 1))
14032     return;
14033
14034   if (ScreenMovPos)
14035   {
14036     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14037     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14038     redraw_mask |= REDRAW_FIELD;
14039   }
14040   else
14041     ScreenMovDir = MV_NONE;
14042 }
14043
14044 void TestIfPlayerTouchesCustomElement(int x, int y)
14045 {
14046   static int xy[4][2] =
14047   {
14048     { 0, -1 },
14049     { -1, 0 },
14050     { +1, 0 },
14051     { 0, +1 }
14052   };
14053   static int trigger_sides[4][2] =
14054   {
14055     /* center side       border side */
14056     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14057     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14058     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14059     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14060   };
14061   static int touch_dir[4] =
14062   {
14063     MV_LEFT | MV_RIGHT,
14064     MV_UP   | MV_DOWN,
14065     MV_UP   | MV_DOWN,
14066     MV_LEFT | MV_RIGHT
14067   };
14068   int center_element = Feld[x][y];      /* should always be non-moving! */
14069   int i;
14070
14071   for (i = 0; i < NUM_DIRECTIONS; i++)
14072   {
14073     int xx = x + xy[i][0];
14074     int yy = y + xy[i][1];
14075     int center_side = trigger_sides[i][0];
14076     int border_side = trigger_sides[i][1];
14077     int border_element;
14078
14079     if (!IN_LEV_FIELD(xx, yy))
14080       continue;
14081
14082     if (IS_PLAYER(x, y))                /* player found at center element */
14083     {
14084       struct PlayerInfo *player = PLAYERINFO(x, y);
14085
14086       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14087         border_element = Feld[xx][yy];          /* may be moving! */
14088       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14089         border_element = Feld[xx][yy];
14090       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14091         border_element = MovingOrBlocked2Element(xx, yy);
14092       else
14093         continue;               /* center and border element do not touch */
14094
14095       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14096                                  player->index_bit, border_side);
14097       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14098                                           CE_PLAYER_TOUCHES_X,
14099                                           player->index_bit, border_side);
14100
14101 #if USE_FIX_CE_ACTION_WITH_PLAYER
14102       {
14103         /* use player element that is initially defined in the level playfield,
14104            not the player element that corresponds to the runtime player number
14105            (example: a level that contains EL_PLAYER_3 as the only player would
14106            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14107         int player_element = PLAYERINFO(x, y)->initial_element;
14108
14109         CheckElementChangeBySide(xx, yy, border_element, player_element,
14110                                  CE_TOUCHING_X, border_side);
14111       }
14112 #endif
14113     }
14114     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14115     {
14116       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14117
14118       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14119       {
14120         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14121           continue;             /* center and border element do not touch */
14122       }
14123
14124       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14125                                  player->index_bit, center_side);
14126       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14127                                           CE_PLAYER_TOUCHES_X,
14128                                           player->index_bit, center_side);
14129
14130 #if USE_FIX_CE_ACTION_WITH_PLAYER
14131       {
14132         /* use player element that is initially defined in the level playfield,
14133            not the player element that corresponds to the runtime player number
14134            (example: a level that contains EL_PLAYER_3 as the only player would
14135            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14136         int player_element = PLAYERINFO(xx, yy)->initial_element;
14137
14138         CheckElementChangeBySide(x, y, center_element, player_element,
14139                                  CE_TOUCHING_X, center_side);
14140       }
14141 #endif
14142
14143       break;
14144     }
14145   }
14146 }
14147
14148 #if USE_ELEMENT_TOUCHING_BUGFIX
14149
14150 void TestIfElementTouchesCustomElement(int x, int y)
14151 {
14152   static int xy[4][2] =
14153   {
14154     { 0, -1 },
14155     { -1, 0 },
14156     { +1, 0 },
14157     { 0, +1 }
14158   };
14159   static int trigger_sides[4][2] =
14160   {
14161     /* center side      border side */
14162     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14163     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14164     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14165     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14166   };
14167   static int touch_dir[4] =
14168   {
14169     MV_LEFT | MV_RIGHT,
14170     MV_UP   | MV_DOWN,
14171     MV_UP   | MV_DOWN,
14172     MV_LEFT | MV_RIGHT
14173   };
14174   boolean change_center_element = FALSE;
14175   int center_element = Feld[x][y];      /* should always be non-moving! */
14176   int border_element_old[NUM_DIRECTIONS];
14177   int i;
14178
14179   for (i = 0; i < NUM_DIRECTIONS; i++)
14180   {
14181     int xx = x + xy[i][0];
14182     int yy = y + xy[i][1];
14183     int border_element;
14184
14185     border_element_old[i] = -1;
14186
14187     if (!IN_LEV_FIELD(xx, yy))
14188       continue;
14189
14190     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14191       border_element = Feld[xx][yy];    /* may be moving! */
14192     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14193       border_element = Feld[xx][yy];
14194     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14195       border_element = MovingOrBlocked2Element(xx, yy);
14196     else
14197       continue;                 /* center and border element do not touch */
14198
14199     border_element_old[i] = border_element;
14200   }
14201
14202   for (i = 0; i < NUM_DIRECTIONS; i++)
14203   {
14204     int xx = x + xy[i][0];
14205     int yy = y + xy[i][1];
14206     int center_side = trigger_sides[i][0];
14207     int border_element = border_element_old[i];
14208
14209     if (border_element == -1)
14210       continue;
14211
14212     /* check for change of border element */
14213     CheckElementChangeBySide(xx, yy, border_element, center_element,
14214                              CE_TOUCHING_X, center_side);
14215
14216     /* (center element cannot be player, so we dont have to check this here) */
14217   }
14218
14219   for (i = 0; i < NUM_DIRECTIONS; i++)
14220   {
14221     int xx = x + xy[i][0];
14222     int yy = y + xy[i][1];
14223     int border_side = trigger_sides[i][1];
14224     int border_element = border_element_old[i];
14225
14226     if (border_element == -1)
14227       continue;
14228
14229     /* check for change of center element (but change it only once) */
14230     if (!change_center_element)
14231       change_center_element =
14232         CheckElementChangeBySide(x, y, center_element, border_element,
14233                                  CE_TOUCHING_X, border_side);
14234
14235 #if USE_FIX_CE_ACTION_WITH_PLAYER
14236     if (IS_PLAYER(xx, yy))
14237     {
14238       /* use player element that is initially defined in the level playfield,
14239          not the player element that corresponds to the runtime player number
14240          (example: a level that contains EL_PLAYER_3 as the only player would
14241          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14242       int player_element = PLAYERINFO(xx, yy)->initial_element;
14243
14244       CheckElementChangeBySide(x, y, center_element, player_element,
14245                                CE_TOUCHING_X, border_side);
14246     }
14247 #endif
14248   }
14249 }
14250
14251 #else
14252
14253 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14254 {
14255   static int xy[4][2] =
14256   {
14257     { 0, -1 },
14258     { -1, 0 },
14259     { +1, 0 },
14260     { 0, +1 }
14261   };
14262   static int trigger_sides[4][2] =
14263   {
14264     /* center side      border side */
14265     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14266     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14267     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14268     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14269   };
14270   static int touch_dir[4] =
14271   {
14272     MV_LEFT | MV_RIGHT,
14273     MV_UP   | MV_DOWN,
14274     MV_UP   | MV_DOWN,
14275     MV_LEFT | MV_RIGHT
14276   };
14277   boolean change_center_element = FALSE;
14278   int center_element = Feld[x][y];      /* should always be non-moving! */
14279   int i;
14280
14281   for (i = 0; i < NUM_DIRECTIONS; i++)
14282   {
14283     int xx = x + xy[i][0];
14284     int yy = y + xy[i][1];
14285     int center_side = trigger_sides[i][0];
14286     int border_side = trigger_sides[i][1];
14287     int border_element;
14288
14289     if (!IN_LEV_FIELD(xx, yy))
14290       continue;
14291
14292     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14293       border_element = Feld[xx][yy];    /* may be moving! */
14294     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14295       border_element = Feld[xx][yy];
14296     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14297       border_element = MovingOrBlocked2Element(xx, yy);
14298     else
14299       continue;                 /* center and border element do not touch */
14300
14301     /* check for change of center element (but change it only once) */
14302     if (!change_center_element)
14303       change_center_element =
14304         CheckElementChangeBySide(x, y, center_element, border_element,
14305                                  CE_TOUCHING_X, border_side);
14306
14307     /* check for change of border element */
14308     CheckElementChangeBySide(xx, yy, border_element, center_element,
14309                              CE_TOUCHING_X, center_side);
14310   }
14311 }
14312
14313 #endif
14314
14315 void TestIfElementHitsCustomElement(int x, int y, int direction)
14316 {
14317   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14318   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14319   int hitx = x + dx, hity = y + dy;
14320   int hitting_element = Feld[x][y];
14321   int touched_element;
14322
14323   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14324     return;
14325
14326   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14327                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14328
14329   if (IN_LEV_FIELD(hitx, hity))
14330   {
14331     int opposite_direction = MV_DIR_OPPOSITE(direction);
14332     int hitting_side = direction;
14333     int touched_side = opposite_direction;
14334     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14335                           MovDir[hitx][hity] != direction ||
14336                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14337
14338     object_hit = TRUE;
14339
14340     if (object_hit)
14341     {
14342       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14343                                CE_HITTING_X, touched_side);
14344
14345       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14346                                CE_HIT_BY_X, hitting_side);
14347
14348       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14349                                CE_HIT_BY_SOMETHING, opposite_direction);
14350
14351 #if USE_FIX_CE_ACTION_WITH_PLAYER
14352       if (IS_PLAYER(hitx, hity))
14353       {
14354         /* use player element that is initially defined in the level playfield,
14355            not the player element that corresponds to the runtime player number
14356            (example: a level that contains EL_PLAYER_3 as the only player would
14357            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14358         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14359
14360         CheckElementChangeBySide(x, y, hitting_element, player_element,
14361                                  CE_HITTING_X, touched_side);
14362       }
14363 #endif
14364     }
14365   }
14366
14367   /* "hitting something" is also true when hitting the playfield border */
14368   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14369                            CE_HITTING_SOMETHING, direction);
14370 }
14371
14372 #if 0
14373 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14374 {
14375   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14376   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14377   int hitx = x + dx, hity = y + dy;
14378   int hitting_element = Feld[x][y];
14379   int touched_element;
14380 #if 0
14381   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14382                         !IS_FREE(hitx, hity) &&
14383                         (!IS_MOVING(hitx, hity) ||
14384                          MovDir[hitx][hity] != direction ||
14385                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14386 #endif
14387
14388   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14389     return;
14390
14391 #if 0
14392   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14393     return;
14394 #endif
14395
14396   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14397                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14398
14399   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14400                            EP_CAN_SMASH_EVERYTHING, direction);
14401
14402   if (IN_LEV_FIELD(hitx, hity))
14403   {
14404     int opposite_direction = MV_DIR_OPPOSITE(direction);
14405     int hitting_side = direction;
14406     int touched_side = opposite_direction;
14407 #if 0
14408     int touched_element = MovingOrBlocked2Element(hitx, hity);
14409 #endif
14410 #if 1
14411     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14412                           MovDir[hitx][hity] != direction ||
14413                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14414
14415     object_hit = TRUE;
14416 #endif
14417
14418     if (object_hit)
14419     {
14420       int i;
14421
14422       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14423                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14424
14425       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14426                                CE_OTHER_IS_SMASHING, touched_side);
14427
14428       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14429                                CE_OTHER_GETS_SMASHED, hitting_side);
14430     }
14431   }
14432 }
14433 #endif
14434
14435 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14436 {
14437   int i, kill_x = -1, kill_y = -1;
14438
14439   int bad_element = -1;
14440   static int test_xy[4][2] =
14441   {
14442     { 0, -1 },
14443     { -1, 0 },
14444     { +1, 0 },
14445     { 0, +1 }
14446   };
14447   static int test_dir[4] =
14448   {
14449     MV_UP,
14450     MV_LEFT,
14451     MV_RIGHT,
14452     MV_DOWN
14453   };
14454
14455   for (i = 0; i < NUM_DIRECTIONS; i++)
14456   {
14457     int test_x, test_y, test_move_dir, test_element;
14458
14459     test_x = good_x + test_xy[i][0];
14460     test_y = good_y + test_xy[i][1];
14461
14462     if (!IN_LEV_FIELD(test_x, test_y))
14463       continue;
14464
14465     test_move_dir =
14466       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14467
14468     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14469
14470     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14471        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14472     */
14473     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14474         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14475     {
14476       kill_x = test_x;
14477       kill_y = test_y;
14478       bad_element = test_element;
14479
14480       break;
14481     }
14482   }
14483
14484   if (kill_x != -1 || kill_y != -1)
14485   {
14486     if (IS_PLAYER(good_x, good_y))
14487     {
14488       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14489
14490       if (player->shield_deadly_time_left > 0 &&
14491           !IS_INDESTRUCTIBLE(bad_element))
14492         Bang(kill_x, kill_y);
14493       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14494         KillPlayer(player);
14495     }
14496     else
14497       Bang(good_x, good_y);
14498   }
14499 }
14500
14501 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14502 {
14503   int i, kill_x = -1, kill_y = -1;
14504   int bad_element = Feld[bad_x][bad_y];
14505   static int test_xy[4][2] =
14506   {
14507     { 0, -1 },
14508     { -1, 0 },
14509     { +1, 0 },
14510     { 0, +1 }
14511   };
14512   static int touch_dir[4] =
14513   {
14514     MV_LEFT | MV_RIGHT,
14515     MV_UP   | MV_DOWN,
14516     MV_UP   | MV_DOWN,
14517     MV_LEFT | MV_RIGHT
14518   };
14519   static int test_dir[4] =
14520   {
14521     MV_UP,
14522     MV_LEFT,
14523     MV_RIGHT,
14524     MV_DOWN
14525   };
14526
14527   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14528     return;
14529
14530   for (i = 0; i < NUM_DIRECTIONS; i++)
14531   {
14532     int test_x, test_y, test_move_dir, test_element;
14533
14534     test_x = bad_x + test_xy[i][0];
14535     test_y = bad_y + test_xy[i][1];
14536
14537     if (!IN_LEV_FIELD(test_x, test_y))
14538       continue;
14539
14540     test_move_dir =
14541       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14542
14543     test_element = Feld[test_x][test_y];
14544
14545     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14546        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14547     */
14548     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14549         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14550     {
14551       /* good thing is player or penguin that does not move away */
14552       if (IS_PLAYER(test_x, test_y))
14553       {
14554         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14555
14556         if (bad_element == EL_ROBOT && player->is_moving)
14557           continue;     /* robot does not kill player if he is moving */
14558
14559         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14560         {
14561           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14562             continue;           /* center and border element do not touch */
14563         }
14564
14565         kill_x = test_x;
14566         kill_y = test_y;
14567
14568         break;
14569       }
14570       else if (test_element == EL_PENGUIN)
14571       {
14572         kill_x = test_x;
14573         kill_y = test_y;
14574
14575         break;
14576       }
14577     }
14578   }
14579
14580   if (kill_x != -1 || kill_y != -1)
14581   {
14582     if (IS_PLAYER(kill_x, kill_y))
14583     {
14584       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14585
14586       if (player->shield_deadly_time_left > 0 &&
14587           !IS_INDESTRUCTIBLE(bad_element))
14588         Bang(bad_x, bad_y);
14589       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14590         KillPlayer(player);
14591     }
14592     else
14593       Bang(kill_x, kill_y);
14594   }
14595 }
14596
14597 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14598 {
14599   int bad_element = Feld[bad_x][bad_y];
14600   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14601   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14602   int test_x = bad_x + dx, test_y = bad_y + dy;
14603   int test_move_dir, test_element;
14604   int kill_x = -1, kill_y = -1;
14605
14606   if (!IN_LEV_FIELD(test_x, test_y))
14607     return;
14608
14609   test_move_dir =
14610     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14611
14612   test_element = Feld[test_x][test_y];
14613
14614   if (test_move_dir != bad_move_dir)
14615   {
14616     /* good thing can be player or penguin that does not move away */
14617     if (IS_PLAYER(test_x, test_y))
14618     {
14619       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14620
14621       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14622          player as being hit when he is moving towards the bad thing, because
14623          the "get hit by" condition would be lost after the player stops) */
14624       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14625         return;         /* player moves away from bad thing */
14626
14627       kill_x = test_x;
14628       kill_y = test_y;
14629     }
14630     else if (test_element == EL_PENGUIN)
14631     {
14632       kill_x = test_x;
14633       kill_y = test_y;
14634     }
14635   }
14636
14637   if (kill_x != -1 || kill_y != -1)
14638   {
14639     if (IS_PLAYER(kill_x, kill_y))
14640     {
14641       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14642
14643       if (player->shield_deadly_time_left > 0 &&
14644           !IS_INDESTRUCTIBLE(bad_element))
14645         Bang(bad_x, bad_y);
14646       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14647         KillPlayer(player);
14648     }
14649     else
14650       Bang(kill_x, kill_y);
14651   }
14652 }
14653
14654 void TestIfPlayerTouchesBadThing(int x, int y)
14655 {
14656   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14657 }
14658
14659 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14660 {
14661   TestIfGoodThingHitsBadThing(x, y, move_dir);
14662 }
14663
14664 void TestIfBadThingTouchesPlayer(int x, int y)
14665 {
14666   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14667 }
14668
14669 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14670 {
14671   TestIfBadThingHitsGoodThing(x, y, move_dir);
14672 }
14673
14674 void TestIfFriendTouchesBadThing(int x, int y)
14675 {
14676   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14677 }
14678
14679 void TestIfBadThingTouchesFriend(int x, int y)
14680 {
14681   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14682 }
14683
14684 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14685 {
14686   int i, kill_x = bad_x, kill_y = bad_y;
14687   static int xy[4][2] =
14688   {
14689     { 0, -1 },
14690     { -1, 0 },
14691     { +1, 0 },
14692     { 0, +1 }
14693   };
14694
14695   for (i = 0; i < NUM_DIRECTIONS; i++)
14696   {
14697     int x, y, element;
14698
14699     x = bad_x + xy[i][0];
14700     y = bad_y + xy[i][1];
14701     if (!IN_LEV_FIELD(x, y))
14702       continue;
14703
14704     element = Feld[x][y];
14705     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14706         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14707     {
14708       kill_x = x;
14709       kill_y = y;
14710       break;
14711     }
14712   }
14713
14714   if (kill_x != bad_x || kill_y != bad_y)
14715     Bang(bad_x, bad_y);
14716 }
14717
14718 void KillPlayer(struct PlayerInfo *player)
14719 {
14720   int jx = player->jx, jy = player->jy;
14721
14722   if (!player->active)
14723     return;
14724
14725 #if 0
14726   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14727          player->killed, player->active, player->reanimated);
14728 #endif
14729
14730   /* the following code was introduced to prevent an infinite loop when calling
14731      -> Bang()
14732      -> CheckTriggeredElementChangeExt()
14733      -> ExecuteCustomElementAction()
14734      -> KillPlayer()
14735      -> (infinitely repeating the above sequence of function calls)
14736      which occurs when killing the player while having a CE with the setting
14737      "kill player X when explosion of <player X>"; the solution using a new
14738      field "player->killed" was chosen for backwards compatibility, although
14739      clever use of the fields "player->active" etc. would probably also work */
14740 #if 1
14741   if (player->killed)
14742     return;
14743 #endif
14744
14745   player->killed = TRUE;
14746
14747   /* remove accessible field at the player's position */
14748   Feld[jx][jy] = EL_EMPTY;
14749
14750   /* deactivate shield (else Bang()/Explode() would not work right) */
14751   player->shield_normal_time_left = 0;
14752   player->shield_deadly_time_left = 0;
14753
14754 #if 0
14755   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14756          player->killed, player->active, player->reanimated);
14757 #endif
14758
14759   Bang(jx, jy);
14760
14761 #if 0
14762   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14763          player->killed, player->active, player->reanimated);
14764 #endif
14765
14766 #if USE_PLAYER_REANIMATION
14767 #if 1
14768   if (player->reanimated)       /* killed player may have been reanimated */
14769     player->killed = player->reanimated = FALSE;
14770   else
14771     BuryPlayer(player);
14772 #else
14773   if (player->killed)           /* player may have been reanimated */
14774     BuryPlayer(player);
14775 #endif
14776 #else
14777   BuryPlayer(player);
14778 #endif
14779 }
14780
14781 static void KillPlayerUnlessEnemyProtected(int x, int y)
14782 {
14783   if (!PLAYER_ENEMY_PROTECTED(x, y))
14784     KillPlayer(PLAYERINFO(x, y));
14785 }
14786
14787 static void KillPlayerUnlessExplosionProtected(int x, int y)
14788 {
14789   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14790     KillPlayer(PLAYERINFO(x, y));
14791 }
14792
14793 void BuryPlayer(struct PlayerInfo *player)
14794 {
14795   int jx = player->jx, jy = player->jy;
14796
14797   if (!player->active)
14798     return;
14799
14800   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14801   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14802
14803   player->GameOver = TRUE;
14804   RemovePlayer(player);
14805 }
14806
14807 void RemovePlayer(struct PlayerInfo *player)
14808 {
14809   int jx = player->jx, jy = player->jy;
14810   int i, found = FALSE;
14811
14812   player->present = FALSE;
14813   player->active = FALSE;
14814
14815   if (!ExplodeField[jx][jy])
14816     StorePlayer[jx][jy] = 0;
14817
14818   if (player->is_moving)
14819     TEST_DrawLevelField(player->last_jx, player->last_jy);
14820
14821   for (i = 0; i < MAX_PLAYERS; i++)
14822     if (stored_player[i].active)
14823       found = TRUE;
14824
14825   if (!found)
14826     AllPlayersGone = TRUE;
14827
14828   ExitX = ZX = jx;
14829   ExitY = ZY = jy;
14830 }
14831
14832 #if USE_NEW_SNAP_DELAY
14833 static void setFieldForSnapping(int x, int y, int element, int direction)
14834 {
14835   struct ElementInfo *ei = &element_info[element];
14836   int direction_bit = MV_DIR_TO_BIT(direction);
14837   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14838   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14839                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14840
14841   Feld[x][y] = EL_ELEMENT_SNAPPING;
14842   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14843
14844   ResetGfxAnimation(x, y);
14845
14846   GfxElement[x][y] = element;
14847   GfxAction[x][y] = action;
14848   GfxDir[x][y] = direction;
14849   GfxFrame[x][y] = -1;
14850 }
14851 #endif
14852
14853 /*
14854   =============================================================================
14855   checkDiagonalPushing()
14856   -----------------------------------------------------------------------------
14857   check if diagonal input device direction results in pushing of object
14858   (by checking if the alternative direction is walkable, diggable, ...)
14859   =============================================================================
14860 */
14861
14862 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14863                                     int x, int y, int real_dx, int real_dy)
14864 {
14865   int jx, jy, dx, dy, xx, yy;
14866
14867   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14868     return TRUE;
14869
14870   /* diagonal direction: check alternative direction */
14871   jx = player->jx;
14872   jy = player->jy;
14873   dx = x - jx;
14874   dy = y - jy;
14875   xx = jx + (dx == 0 ? real_dx : 0);
14876   yy = jy + (dy == 0 ? real_dy : 0);
14877
14878   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14879 }
14880
14881 /*
14882   =============================================================================
14883   DigField()
14884   -----------------------------------------------------------------------------
14885   x, y:                 field next to player (non-diagonal) to try to dig to
14886   real_dx, real_dy:     direction as read from input device (can be diagonal)
14887   =============================================================================
14888 */
14889
14890 static int DigField(struct PlayerInfo *player,
14891                     int oldx, int oldy, int x, int y,
14892                     int real_dx, int real_dy, int mode)
14893 {
14894   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14895   boolean player_was_pushing = player->is_pushing;
14896   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14897   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14898   int jx = oldx, jy = oldy;
14899   int dx = x - jx, dy = y - jy;
14900   int nextx = x + dx, nexty = y + dy;
14901   int move_direction = (dx == -1 ? MV_LEFT  :
14902                         dx == +1 ? MV_RIGHT :
14903                         dy == -1 ? MV_UP    :
14904                         dy == +1 ? MV_DOWN  : MV_NONE);
14905   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14906   int dig_side = MV_DIR_OPPOSITE(move_direction);
14907   int old_element = Feld[jx][jy];
14908 #if USE_FIXED_DONT_RUN_INTO
14909   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14910 #else
14911   int element;
14912 #endif
14913   int collect_count;
14914
14915   if (is_player)                /* function can also be called by EL_PENGUIN */
14916   {
14917     if (player->MovPos == 0)
14918     {
14919       player->is_digging = FALSE;
14920       player->is_collecting = FALSE;
14921     }
14922
14923     if (player->MovPos == 0)    /* last pushing move finished */
14924       player->is_pushing = FALSE;
14925
14926     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14927     {
14928       player->is_switching = FALSE;
14929       player->push_delay = -1;
14930
14931       return MP_NO_ACTION;
14932     }
14933   }
14934
14935 #if !USE_FIXED_DONT_RUN_INTO
14936   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14937     return MP_NO_ACTION;
14938 #endif
14939
14940   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14941     old_element = Back[jx][jy];
14942
14943   /* in case of element dropped at player position, check background */
14944   else if (Back[jx][jy] != EL_EMPTY &&
14945            game.engine_version >= VERSION_IDENT(2,2,0,0))
14946     old_element = Back[jx][jy];
14947
14948   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14949     return MP_NO_ACTION;        /* field has no opening in this direction */
14950
14951   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14952     return MP_NO_ACTION;        /* field has no opening in this direction */
14953
14954 #if USE_FIXED_DONT_RUN_INTO
14955   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14956   {
14957     SplashAcid(x, y);
14958
14959     Feld[jx][jy] = player->artwork_element;
14960     InitMovingField(jx, jy, MV_DOWN);
14961     Store[jx][jy] = EL_ACID;
14962     ContinueMoving(jx, jy);
14963     BuryPlayer(player);
14964
14965     return MP_DONT_RUN_INTO;
14966   }
14967 #endif
14968
14969 #if USE_FIXED_DONT_RUN_INTO
14970   if (player_can_move && DONT_RUN_INTO(element))
14971   {
14972     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14973
14974     return MP_DONT_RUN_INTO;
14975   }
14976 #endif
14977
14978 #if USE_FIXED_DONT_RUN_INTO
14979   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14980     return MP_NO_ACTION;
14981 #endif
14982
14983 #if !USE_FIXED_DONT_RUN_INTO
14984   element = Feld[x][y];
14985 #endif
14986
14987   collect_count = element_info[element].collect_count_initial;
14988
14989   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14990     return MP_NO_ACTION;
14991
14992   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14993     player_can_move = player_can_move_or_snap;
14994
14995   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14996       game.engine_version >= VERSION_IDENT(2,2,0,0))
14997   {
14998     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14999                                player->index_bit, dig_side);
15000     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15001                                         player->index_bit, dig_side);
15002
15003     if (element == EL_DC_LANDMINE)
15004       Bang(x, y);
15005
15006     if (Feld[x][y] != element)          /* field changed by snapping */
15007       return MP_ACTION;
15008
15009     return MP_NO_ACTION;
15010   }
15011
15012 #if USE_PLAYER_GRAVITY
15013   if (player->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 #else
15018   if (game.gravity && is_player && !player->is_auto_moving &&
15019       canFallDown(player) && move_direction != MV_DOWN &&
15020       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15021     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15022 #endif
15023
15024   if (player_can_move &&
15025       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15026   {
15027     int sound_element = SND_ELEMENT(element);
15028     int sound_action = ACTION_WALKING;
15029
15030     if (IS_RND_GATE(element))
15031     {
15032       if (!player->key[RND_GATE_NR(element)])
15033         return MP_NO_ACTION;
15034     }
15035     else if (IS_RND_GATE_GRAY(element))
15036     {
15037       if (!player->key[RND_GATE_GRAY_NR(element)])
15038         return MP_NO_ACTION;
15039     }
15040     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15041     {
15042       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15043         return MP_NO_ACTION;
15044     }
15045     else if (element == EL_EXIT_OPEN ||
15046              element == EL_EM_EXIT_OPEN ||
15047 #if 1
15048              element == EL_EM_EXIT_OPENING ||
15049 #endif
15050              element == EL_STEEL_EXIT_OPEN ||
15051              element == EL_EM_STEEL_EXIT_OPEN ||
15052 #if 1
15053              element == EL_EM_STEEL_EXIT_OPENING ||
15054 #endif
15055              element == EL_SP_EXIT_OPEN ||
15056              element == EL_SP_EXIT_OPENING)
15057     {
15058       sound_action = ACTION_PASSING;    /* player is passing exit */
15059     }
15060     else if (element == EL_EMPTY)
15061     {
15062       sound_action = ACTION_MOVING;             /* nothing to walk on */
15063     }
15064
15065     /* play sound from background or player, whatever is available */
15066     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15067       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15068     else
15069       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15070   }
15071   else if (player_can_move &&
15072            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15073   {
15074     if (!ACCESS_FROM(element, opposite_direction))
15075       return MP_NO_ACTION;      /* field not accessible from this direction */
15076
15077     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15078       return MP_NO_ACTION;
15079
15080     if (IS_EM_GATE(element))
15081     {
15082       if (!player->key[EM_GATE_NR(element)])
15083         return MP_NO_ACTION;
15084     }
15085     else if (IS_EM_GATE_GRAY(element))
15086     {
15087       if (!player->key[EM_GATE_GRAY_NR(element)])
15088         return MP_NO_ACTION;
15089     }
15090     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15091     {
15092       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15093         return MP_NO_ACTION;
15094     }
15095     else if (IS_EMC_GATE(element))
15096     {
15097       if (!player->key[EMC_GATE_NR(element)])
15098         return MP_NO_ACTION;
15099     }
15100     else if (IS_EMC_GATE_GRAY(element))
15101     {
15102       if (!player->key[EMC_GATE_GRAY_NR(element)])
15103         return MP_NO_ACTION;
15104     }
15105     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15106     {
15107       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15108         return MP_NO_ACTION;
15109     }
15110     else if (element == EL_DC_GATE_WHITE ||
15111              element == EL_DC_GATE_WHITE_GRAY ||
15112              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15113     {
15114       if (player->num_white_keys == 0)
15115         return MP_NO_ACTION;
15116
15117       player->num_white_keys--;
15118     }
15119     else if (IS_SP_PORT(element))
15120     {
15121       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15122           element == EL_SP_GRAVITY_PORT_RIGHT ||
15123           element == EL_SP_GRAVITY_PORT_UP ||
15124           element == EL_SP_GRAVITY_PORT_DOWN)
15125 #if USE_PLAYER_GRAVITY
15126         player->gravity = !player->gravity;
15127 #else
15128         game.gravity = !game.gravity;
15129 #endif
15130       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15131                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15132                element == EL_SP_GRAVITY_ON_PORT_UP ||
15133                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15134 #if USE_PLAYER_GRAVITY
15135         player->gravity = TRUE;
15136 #else
15137         game.gravity = TRUE;
15138 #endif
15139       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15140                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15141                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15142                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15143 #if USE_PLAYER_GRAVITY
15144         player->gravity = FALSE;
15145 #else
15146         game.gravity = FALSE;
15147 #endif
15148     }
15149
15150     /* automatically move to the next field with double speed */
15151     player->programmed_action = move_direction;
15152
15153     if (player->move_delay_reset_counter == 0)
15154     {
15155       player->move_delay_reset_counter = 2;     /* two double speed steps */
15156
15157       DOUBLE_PLAYER_SPEED(player);
15158     }
15159
15160     PlayLevelSoundAction(x, y, ACTION_PASSING);
15161   }
15162   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15163   {
15164     RemoveField(x, y);
15165
15166     if (mode != DF_SNAP)
15167     {
15168       GfxElement[x][y] = GFX_ELEMENT(element);
15169       player->is_digging = TRUE;
15170     }
15171
15172     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15173
15174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15175                                         player->index_bit, dig_side);
15176
15177     if (mode == DF_SNAP)
15178     {
15179 #if USE_NEW_SNAP_DELAY
15180       if (level.block_snap_field)
15181         setFieldForSnapping(x, y, element, move_direction);
15182       else
15183         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15184 #else
15185       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15186 #endif
15187
15188       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15189                                           player->index_bit, dig_side);
15190     }
15191   }
15192   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15193   {
15194     RemoveField(x, y);
15195
15196     if (is_player && mode != DF_SNAP)
15197     {
15198       GfxElement[x][y] = element;
15199       player->is_collecting = TRUE;
15200     }
15201
15202     if (element == EL_SPEED_PILL)
15203     {
15204       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15205     }
15206     else if (element == EL_EXTRA_TIME && level.time > 0)
15207     {
15208       TimeLeft += level.extra_time;
15209
15210 #if 1
15211       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15212
15213       DisplayGameControlValues();
15214 #else
15215       DrawGameValue_Time(TimeLeft);
15216 #endif
15217     }
15218     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15219     {
15220       player->shield_normal_time_left += level.shield_normal_time;
15221       if (element == EL_SHIELD_DEADLY)
15222         player->shield_deadly_time_left += level.shield_deadly_time;
15223     }
15224     else if (element == EL_DYNAMITE ||
15225              element == EL_EM_DYNAMITE ||
15226              element == EL_SP_DISK_RED)
15227     {
15228       if (player->inventory_size < MAX_INVENTORY_SIZE)
15229         player->inventory_element[player->inventory_size++] = element;
15230
15231       DrawGameDoorValues();
15232     }
15233     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15234     {
15235       player->dynabomb_count++;
15236       player->dynabombs_left++;
15237     }
15238     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15239     {
15240       player->dynabomb_size++;
15241     }
15242     else if (element == EL_DYNABOMB_INCREASE_POWER)
15243     {
15244       player->dynabomb_xl = TRUE;
15245     }
15246     else if (IS_KEY(element))
15247     {
15248       player->key[KEY_NR(element)] = TRUE;
15249
15250       DrawGameDoorValues();
15251     }
15252     else if (element == EL_DC_KEY_WHITE)
15253     {
15254       player->num_white_keys++;
15255
15256       /* display white keys? */
15257       /* DrawGameDoorValues(); */
15258     }
15259     else if (IS_ENVELOPE(element))
15260     {
15261       player->show_envelope = element;
15262     }
15263     else if (element == EL_EMC_LENSES)
15264     {
15265       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15266
15267       RedrawAllInvisibleElementsForLenses();
15268     }
15269     else if (element == EL_EMC_MAGNIFIER)
15270     {
15271       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15272
15273       RedrawAllInvisibleElementsForMagnifier();
15274     }
15275     else if (IS_DROPPABLE(element) ||
15276              IS_THROWABLE(element))     /* can be collected and dropped */
15277     {
15278       int i;
15279
15280       if (collect_count == 0)
15281         player->inventory_infinite_element = element;
15282       else
15283         for (i = 0; i < collect_count; i++)
15284           if (player->inventory_size < MAX_INVENTORY_SIZE)
15285             player->inventory_element[player->inventory_size++] = element;
15286
15287       DrawGameDoorValues();
15288     }
15289     else if (collect_count > 0)
15290     {
15291       local_player->gems_still_needed -= collect_count;
15292       if (local_player->gems_still_needed < 0)
15293         local_player->gems_still_needed = 0;
15294
15295 #if 1
15296       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15297
15298       DisplayGameControlValues();
15299 #else
15300       DrawGameValue_Emeralds(local_player->gems_still_needed);
15301 #endif
15302     }
15303
15304     RaiseScoreElement(element);
15305     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15306
15307     if (is_player)
15308       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15309                                           player->index_bit, dig_side);
15310
15311     if (mode == DF_SNAP)
15312     {
15313 #if USE_NEW_SNAP_DELAY
15314       if (level.block_snap_field)
15315         setFieldForSnapping(x, y, element, move_direction);
15316       else
15317         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15318 #else
15319       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15320 #endif
15321
15322       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15323                                           player->index_bit, dig_side);
15324     }
15325   }
15326   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15327   {
15328     if (mode == DF_SNAP && element != EL_BD_ROCK)
15329       return MP_NO_ACTION;
15330
15331     if (CAN_FALL(element) && dy)
15332       return MP_NO_ACTION;
15333
15334     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15335         !(element == EL_SPRING && level.use_spring_bug))
15336       return MP_NO_ACTION;
15337
15338     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15339         ((move_direction & MV_VERTICAL &&
15340           ((element_info[element].move_pattern & MV_LEFT &&
15341             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15342            (element_info[element].move_pattern & MV_RIGHT &&
15343             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15344          (move_direction & MV_HORIZONTAL &&
15345           ((element_info[element].move_pattern & MV_UP &&
15346             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15347            (element_info[element].move_pattern & MV_DOWN &&
15348             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15349       return MP_NO_ACTION;
15350
15351     /* do not push elements already moving away faster than player */
15352     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15353         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15354       return MP_NO_ACTION;
15355
15356     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15357     {
15358       if (player->push_delay_value == -1 || !player_was_pushing)
15359         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15360     }
15361     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15362     {
15363       if (player->push_delay_value == -1)
15364         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15365     }
15366     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15367     {
15368       if (!player->is_pushing)
15369         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15370     }
15371
15372     player->is_pushing = TRUE;
15373     player->is_active = TRUE;
15374
15375     if (!(IN_LEV_FIELD(nextx, nexty) &&
15376           (IS_FREE(nextx, nexty) ||
15377            (IS_SB_ELEMENT(element) &&
15378             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15379            (IS_CUSTOM_ELEMENT(element) &&
15380             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15381       return MP_NO_ACTION;
15382
15383     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15384       return MP_NO_ACTION;
15385
15386     if (player->push_delay == -1)       /* new pushing; restart delay */
15387       player->push_delay = 0;
15388
15389     if (player->push_delay < player->push_delay_value &&
15390         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15391         element != EL_SPRING && element != EL_BALLOON)
15392     {
15393       /* make sure that there is no move delay before next try to push */
15394       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15395         player->move_delay = 0;
15396
15397       return MP_NO_ACTION;
15398     }
15399
15400     if (IS_CUSTOM_ELEMENT(element) &&
15401         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15402     {
15403       if (!DigFieldByCE(nextx, nexty, element))
15404         return MP_NO_ACTION;
15405     }
15406
15407     if (IS_SB_ELEMENT(element))
15408     {
15409       if (element == EL_SOKOBAN_FIELD_FULL)
15410       {
15411         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15412         local_player->sokobanfields_still_needed++;
15413       }
15414
15415       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15416       {
15417         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15418         local_player->sokobanfields_still_needed--;
15419       }
15420
15421       Feld[x][y] = EL_SOKOBAN_OBJECT;
15422
15423       if (Back[x][y] == Back[nextx][nexty])
15424         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15425       else if (Back[x][y] != 0)
15426         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15427                                     ACTION_EMPTYING);
15428       else
15429         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15430                                     ACTION_FILLING);
15431
15432 #if 1
15433       if (local_player->sokobanfields_still_needed == 0 &&
15434           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15435 #else
15436       if (local_player->sokobanfields_still_needed == 0 &&
15437           game.emulation == EMU_SOKOBAN)
15438 #endif
15439       {
15440         PlayerWins(player);
15441
15442         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15443       }
15444     }
15445     else
15446       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15447
15448     InitMovingField(x, y, move_direction);
15449     GfxAction[x][y] = ACTION_PUSHING;
15450
15451     if (mode == DF_SNAP)
15452       ContinueMoving(x, y);
15453     else
15454       MovPos[x][y] = (dx != 0 ? dx : dy);
15455
15456     Pushed[x][y] = TRUE;
15457     Pushed[nextx][nexty] = TRUE;
15458
15459     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15460       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15461     else
15462       player->push_delay_value = -1;    /* get new value later */
15463
15464     /* check for element change _after_ element has been pushed */
15465     if (game.use_change_when_pushing_bug)
15466     {
15467       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15468                                  player->index_bit, dig_side);
15469       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15470                                           player->index_bit, dig_side);
15471     }
15472   }
15473   else if (IS_SWITCHABLE(element))
15474   {
15475     if (PLAYER_SWITCHING(player, x, y))
15476     {
15477       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15478                                           player->index_bit, dig_side);
15479
15480       return MP_ACTION;
15481     }
15482
15483     player->is_switching = TRUE;
15484     player->switch_x = x;
15485     player->switch_y = y;
15486
15487     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15488
15489     if (element == EL_ROBOT_WHEEL)
15490     {
15491       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15492       ZX = x;
15493       ZY = y;
15494
15495       game.robot_wheel_active = TRUE;
15496
15497       TEST_DrawLevelField(x, y);
15498     }
15499     else if (element == EL_SP_TERMINAL)
15500     {
15501       int xx, yy;
15502
15503       SCAN_PLAYFIELD(xx, yy)
15504       {
15505         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15506           Bang(xx, yy);
15507         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15508           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15509       }
15510     }
15511     else if (IS_BELT_SWITCH(element))
15512     {
15513       ToggleBeltSwitch(x, y);
15514     }
15515     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15516              element == EL_SWITCHGATE_SWITCH_DOWN ||
15517              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15518              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15519     {
15520       ToggleSwitchgateSwitch(x, y);
15521     }
15522     else if (element == EL_LIGHT_SWITCH ||
15523              element == EL_LIGHT_SWITCH_ACTIVE)
15524     {
15525       ToggleLightSwitch(x, y);
15526     }
15527     else if (element == EL_TIMEGATE_SWITCH ||
15528              element == EL_DC_TIMEGATE_SWITCH)
15529     {
15530       ActivateTimegateSwitch(x, y);
15531     }
15532     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15533              element == EL_BALLOON_SWITCH_RIGHT ||
15534              element == EL_BALLOON_SWITCH_UP    ||
15535              element == EL_BALLOON_SWITCH_DOWN  ||
15536              element == EL_BALLOON_SWITCH_NONE  ||
15537              element == EL_BALLOON_SWITCH_ANY)
15538     {
15539       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15540                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15541                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15542                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15543                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15544                              move_direction);
15545     }
15546     else if (element == EL_LAMP)
15547     {
15548       Feld[x][y] = EL_LAMP_ACTIVE;
15549       local_player->lights_still_needed--;
15550
15551       ResetGfxAnimation(x, y);
15552       TEST_DrawLevelField(x, y);
15553     }
15554     else if (element == EL_TIME_ORB_FULL)
15555     {
15556       Feld[x][y] = EL_TIME_ORB_EMPTY;
15557
15558       if (level.time > 0 || level.use_time_orb_bug)
15559       {
15560         TimeLeft += level.time_orb_time;
15561
15562 #if 1
15563         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15564
15565         DisplayGameControlValues();
15566 #else
15567         DrawGameValue_Time(TimeLeft);
15568 #endif
15569       }
15570
15571       ResetGfxAnimation(x, y);
15572       TEST_DrawLevelField(x, y);
15573     }
15574     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15575              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15576     {
15577       int xx, yy;
15578
15579       game.ball_state = !game.ball_state;
15580
15581       SCAN_PLAYFIELD(xx, yy)
15582       {
15583         int e = Feld[xx][yy];
15584
15585         if (game.ball_state)
15586         {
15587           if (e == EL_EMC_MAGIC_BALL)
15588             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15589           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15590             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15591         }
15592         else
15593         {
15594           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15595             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15596           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15597             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15598         }
15599       }
15600     }
15601
15602     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15603                                         player->index_bit, dig_side);
15604
15605     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15606                                         player->index_bit, dig_side);
15607
15608     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15609                                         player->index_bit, dig_side);
15610
15611     return MP_ACTION;
15612   }
15613   else
15614   {
15615     if (!PLAYER_SWITCHING(player, x, y))
15616     {
15617       player->is_switching = TRUE;
15618       player->switch_x = x;
15619       player->switch_y = y;
15620
15621       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15622                                  player->index_bit, dig_side);
15623       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15624                                           player->index_bit, dig_side);
15625
15626       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15627                                  player->index_bit, dig_side);
15628       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15629                                           player->index_bit, dig_side);
15630     }
15631
15632     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15633                                player->index_bit, dig_side);
15634     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15635                                         player->index_bit, dig_side);
15636
15637     return MP_NO_ACTION;
15638   }
15639
15640   player->push_delay = -1;
15641
15642   if (is_player)                /* function can also be called by EL_PENGUIN */
15643   {
15644     if (Feld[x][y] != element)          /* really digged/collected something */
15645     {
15646       player->is_collecting = !player->is_digging;
15647       player->is_active = TRUE;
15648     }
15649   }
15650
15651   return MP_MOVING;
15652 }
15653
15654 static boolean DigFieldByCE(int x, int y, int digging_element)
15655 {
15656   int element = Feld[x][y];
15657
15658   if (!IS_FREE(x, y))
15659   {
15660     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15661                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15662                   ACTION_BREAKING);
15663
15664     /* no element can dig solid indestructible elements */
15665     if (IS_INDESTRUCTIBLE(element) &&
15666         !IS_DIGGABLE(element) &&
15667         !IS_COLLECTIBLE(element))
15668       return FALSE;
15669
15670     if (AmoebaNr[x][y] &&
15671         (element == EL_AMOEBA_FULL ||
15672          element == EL_BD_AMOEBA ||
15673          element == EL_AMOEBA_GROWING))
15674     {
15675       AmoebaCnt[AmoebaNr[x][y]]--;
15676       AmoebaCnt2[AmoebaNr[x][y]]--;
15677     }
15678
15679     if (IS_MOVING(x, y))
15680       RemoveMovingField(x, y);
15681     else
15682     {
15683       RemoveField(x, y);
15684       TEST_DrawLevelField(x, y);
15685     }
15686
15687     /* if digged element was about to explode, prevent the explosion */
15688     ExplodeField[x][y] = EX_TYPE_NONE;
15689
15690     PlayLevelSoundAction(x, y, action);
15691   }
15692
15693   Store[x][y] = EL_EMPTY;
15694
15695 #if 1
15696   /* this makes it possible to leave the removed element again */
15697   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15698     Store[x][y] = element;
15699 #else
15700   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15701   {
15702     int move_leave_element = element_info[digging_element].move_leave_element;
15703
15704     /* this makes it possible to leave the removed element again */
15705     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15706                    element : move_leave_element);
15707   }
15708 #endif
15709
15710   return TRUE;
15711 }
15712
15713 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15714 {
15715   int jx = player->jx, jy = player->jy;
15716   int x = jx + dx, y = jy + dy;
15717   int snap_direction = (dx == -1 ? MV_LEFT  :
15718                         dx == +1 ? MV_RIGHT :
15719                         dy == -1 ? MV_UP    :
15720                         dy == +1 ? MV_DOWN  : MV_NONE);
15721   boolean can_continue_snapping = (level.continuous_snapping &&
15722                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15723
15724   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15725     return FALSE;
15726
15727   if (!player->active || !IN_LEV_FIELD(x, y))
15728     return FALSE;
15729
15730   if (dx && dy)
15731     return FALSE;
15732
15733   if (!dx && !dy)
15734   {
15735     if (player->MovPos == 0)
15736       player->is_pushing = FALSE;
15737
15738     player->is_snapping = FALSE;
15739
15740     if (player->MovPos == 0)
15741     {
15742       player->is_moving = FALSE;
15743       player->is_digging = FALSE;
15744       player->is_collecting = FALSE;
15745     }
15746
15747     return FALSE;
15748   }
15749
15750 #if USE_NEW_CONTINUOUS_SNAPPING
15751   /* prevent snapping with already pressed snap key when not allowed */
15752   if (player->is_snapping && !can_continue_snapping)
15753     return FALSE;
15754 #else
15755   if (player->is_snapping)
15756     return FALSE;
15757 #endif
15758
15759   player->MovDir = snap_direction;
15760
15761   if (player->MovPos == 0)
15762   {
15763     player->is_moving = FALSE;
15764     player->is_digging = FALSE;
15765     player->is_collecting = FALSE;
15766   }
15767
15768   player->is_dropping = FALSE;
15769   player->is_dropping_pressed = FALSE;
15770   player->drop_pressed_delay = 0;
15771
15772   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15773     return FALSE;
15774
15775   player->is_snapping = TRUE;
15776   player->is_active = TRUE;
15777
15778   if (player->MovPos == 0)
15779   {
15780     player->is_moving = FALSE;
15781     player->is_digging = FALSE;
15782     player->is_collecting = FALSE;
15783   }
15784
15785   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15786     TEST_DrawLevelField(player->last_jx, player->last_jy);
15787
15788   TEST_DrawLevelField(x, y);
15789
15790   return TRUE;
15791 }
15792
15793 static boolean DropElement(struct PlayerInfo *player)
15794 {
15795   int old_element, new_element;
15796   int dropx = player->jx, dropy = player->jy;
15797   int drop_direction = player->MovDir;
15798   int drop_side = drop_direction;
15799 #if 1
15800   int drop_element = get_next_dropped_element(player);
15801 #else
15802   int drop_element = (player->inventory_size > 0 ?
15803                       player->inventory_element[player->inventory_size - 1] :
15804                       player->inventory_infinite_element != EL_UNDEFINED ?
15805                       player->inventory_infinite_element :
15806                       player->dynabombs_left > 0 ?
15807                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15808                       EL_UNDEFINED);
15809 #endif
15810
15811   player->is_dropping_pressed = TRUE;
15812
15813   /* do not drop an element on top of another element; when holding drop key
15814      pressed without moving, dropped element must move away before the next
15815      element can be dropped (this is especially important if the next element
15816      is dynamite, which can be placed on background for historical reasons) */
15817   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15818     return MP_ACTION;
15819
15820   if (IS_THROWABLE(drop_element))
15821   {
15822     dropx += GET_DX_FROM_DIR(drop_direction);
15823     dropy += GET_DY_FROM_DIR(drop_direction);
15824
15825     if (!IN_LEV_FIELD(dropx, dropy))
15826       return FALSE;
15827   }
15828
15829   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15830   new_element = drop_element;           /* default: no change when dropping */
15831
15832   /* check if player is active, not moving and ready to drop */
15833   if (!player->active || player->MovPos || player->drop_delay > 0)
15834     return FALSE;
15835
15836   /* check if player has anything that can be dropped */
15837   if (new_element == EL_UNDEFINED)
15838     return FALSE;
15839
15840   /* check if drop key was pressed long enough for EM style dynamite */
15841   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15842     return FALSE;
15843
15844   /* check if anything can be dropped at the current position */
15845   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15846     return FALSE;
15847
15848   /* collected custom elements can only be dropped on empty fields */
15849   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15850     return FALSE;
15851
15852   if (old_element != EL_EMPTY)
15853     Back[dropx][dropy] = old_element;   /* store old element on this field */
15854
15855   ResetGfxAnimation(dropx, dropy);
15856   ResetRandomAnimationValue(dropx, dropy);
15857
15858   if (player->inventory_size > 0 ||
15859       player->inventory_infinite_element != EL_UNDEFINED)
15860   {
15861     if (player->inventory_size > 0)
15862     {
15863       player->inventory_size--;
15864
15865       DrawGameDoorValues();
15866
15867       if (new_element == EL_DYNAMITE)
15868         new_element = EL_DYNAMITE_ACTIVE;
15869       else if (new_element == EL_EM_DYNAMITE)
15870         new_element = EL_EM_DYNAMITE_ACTIVE;
15871       else if (new_element == EL_SP_DISK_RED)
15872         new_element = EL_SP_DISK_RED_ACTIVE;
15873     }
15874
15875     Feld[dropx][dropy] = new_element;
15876
15877     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15878       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15879                           el2img(Feld[dropx][dropy]), 0);
15880
15881     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15882
15883     /* needed if previous element just changed to "empty" in the last frame */
15884     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15885
15886     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15887                                player->index_bit, drop_side);
15888     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15889                                         CE_PLAYER_DROPS_X,
15890                                         player->index_bit, drop_side);
15891
15892     TestIfElementTouchesCustomElement(dropx, dropy);
15893   }
15894   else          /* player is dropping a dyna bomb */
15895   {
15896     player->dynabombs_left--;
15897
15898     Feld[dropx][dropy] = new_element;
15899
15900     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15901       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15902                           el2img(Feld[dropx][dropy]), 0);
15903
15904     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15905   }
15906
15907   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15908     InitField_WithBug1(dropx, dropy, FALSE);
15909
15910   new_element = Feld[dropx][dropy];     /* element might have changed */
15911
15912   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15913       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15914   {
15915     int move_direction, nextx, nexty;
15916
15917     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15918       MovDir[dropx][dropy] = drop_direction;
15919
15920     move_direction = MovDir[dropx][dropy];
15921     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15922     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15923
15924     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15925
15926 #if USE_FIX_IMPACT_COLLISION
15927     /* do not cause impact style collision by dropping elements that can fall */
15928     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15929 #else
15930     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15931 #endif
15932   }
15933
15934   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15935   player->is_dropping = TRUE;
15936
15937   player->drop_pressed_delay = 0;
15938   player->is_dropping_pressed = FALSE;
15939
15940   player->drop_x = dropx;
15941   player->drop_y = dropy;
15942
15943   return TRUE;
15944 }
15945
15946 /* ------------------------------------------------------------------------- */
15947 /* game sound playing functions                                              */
15948 /* ------------------------------------------------------------------------- */
15949
15950 static int *loop_sound_frame = NULL;
15951 static int *loop_sound_volume = NULL;
15952
15953 void InitPlayLevelSound()
15954 {
15955   int num_sounds = getSoundListSize();
15956
15957   checked_free(loop_sound_frame);
15958   checked_free(loop_sound_volume);
15959
15960   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15961   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15962 }
15963
15964 static void PlayLevelSound(int x, int y, int nr)
15965 {
15966   int sx = SCREENX(x), sy = SCREENY(y);
15967   int volume, stereo_position;
15968   int max_distance = 8;
15969   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15970
15971   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15972       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15973     return;
15974
15975   if (!IN_LEV_FIELD(x, y) ||
15976       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15977       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15978     return;
15979
15980   volume = SOUND_MAX_VOLUME;
15981
15982   if (!IN_SCR_FIELD(sx, sy))
15983   {
15984     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15985     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15986
15987     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15988   }
15989
15990   stereo_position = (SOUND_MAX_LEFT +
15991                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15992                      (SCR_FIELDX + 2 * max_distance));
15993
15994   if (IS_LOOP_SOUND(nr))
15995   {
15996     /* This assures that quieter loop sounds do not overwrite louder ones,
15997        while restarting sound volume comparison with each new game frame. */
15998
15999     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16000       return;
16001
16002     loop_sound_volume[nr] = volume;
16003     loop_sound_frame[nr] = FrameCounter;
16004   }
16005
16006   PlaySoundExt(nr, volume, stereo_position, type);
16007 }
16008
16009 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16010 {
16011   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16012                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16013                  y < LEVELY(BY1) ? LEVELY(BY1) :
16014                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16015                  sound_action);
16016 }
16017
16018 static void PlayLevelSoundAction(int x, int y, int action)
16019 {
16020   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16021 }
16022
16023 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16024 {
16025   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16026
16027   if (sound_effect != SND_UNDEFINED)
16028     PlayLevelSound(x, y, sound_effect);
16029 }
16030
16031 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16032                                               int action)
16033 {
16034   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16035
16036   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16037     PlayLevelSound(x, y, sound_effect);
16038 }
16039
16040 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16041 {
16042   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16043
16044   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16045     PlayLevelSound(x, y, sound_effect);
16046 }
16047
16048 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16049 {
16050   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16051
16052   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16053     StopSound(sound_effect);
16054 }
16055
16056 static void PlayLevelMusic()
16057 {
16058   if (levelset.music[level_nr] != MUS_UNDEFINED)
16059     PlayMusic(levelset.music[level_nr]);        /* from config file */
16060   else
16061     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16062 }
16063
16064 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16065 {
16066   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16067   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16068   int x = xx - 1 - offset;
16069   int y = yy - 1 - offset;
16070
16071   switch (sample)
16072   {
16073     case SAMPLE_blank:
16074       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16075       break;
16076
16077     case SAMPLE_roll:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16079       break;
16080
16081     case SAMPLE_stone:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16083       break;
16084
16085     case SAMPLE_nut:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16087       break;
16088
16089     case SAMPLE_crack:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16091       break;
16092
16093     case SAMPLE_bug:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16095       break;
16096
16097     case SAMPLE_tank:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16099       break;
16100
16101     case SAMPLE_android_clone:
16102       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16103       break;
16104
16105     case SAMPLE_android_move:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16107       break;
16108
16109     case SAMPLE_spring:
16110       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16111       break;
16112
16113     case SAMPLE_slurp:
16114       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16115       break;
16116
16117     case SAMPLE_eater:
16118       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16119       break;
16120
16121     case SAMPLE_eater_eat:
16122       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16123       break;
16124
16125     case SAMPLE_alien:
16126       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16127       break;
16128
16129     case SAMPLE_collect:
16130       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16131       break;
16132
16133     case SAMPLE_diamond:
16134       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16135       break;
16136
16137     case SAMPLE_squash:
16138       /* !!! CHECK THIS !!! */
16139 #if 1
16140       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16141 #else
16142       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16143 #endif
16144       break;
16145
16146     case SAMPLE_wonderfall:
16147       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16148       break;
16149
16150     case SAMPLE_drip:
16151       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16152       break;
16153
16154     case SAMPLE_push:
16155       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16156       break;
16157
16158     case SAMPLE_dirt:
16159       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16160       break;
16161
16162     case SAMPLE_acid:
16163       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16164       break;
16165
16166     case SAMPLE_ball:
16167       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16168       break;
16169
16170     case SAMPLE_grow:
16171       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16172       break;
16173
16174     case SAMPLE_wonder:
16175       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16176       break;
16177
16178     case SAMPLE_door:
16179       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16180       break;
16181
16182     case SAMPLE_exit_open:
16183       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16184       break;
16185
16186     case SAMPLE_exit_leave:
16187       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16188       break;
16189
16190     case SAMPLE_dynamite:
16191       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16192       break;
16193
16194     case SAMPLE_tick:
16195       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16196       break;
16197
16198     case SAMPLE_press:
16199       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16200       break;
16201
16202     case SAMPLE_wheel:
16203       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16204       break;
16205
16206     case SAMPLE_boom:
16207       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16208       break;
16209
16210     case SAMPLE_die:
16211       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16212       break;
16213
16214     case SAMPLE_time:
16215       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16216       break;
16217
16218     default:
16219       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16220       break;
16221   }
16222 }
16223
16224 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16225 {
16226   int element = map_element_SP_to_RND(element_sp);
16227   int action = map_action_SP_to_RND(action_sp);
16228   int offset = (setup.sp_show_border_elements ? 0 : 1);
16229   int x = xx - offset;
16230   int y = yy - offset;
16231
16232 #if 0
16233   printf("::: %d -> %d\n", element_sp, action_sp);
16234 #endif
16235
16236   PlayLevelSoundElementAction(x, y, element, action);
16237 }
16238
16239 #if 0
16240 void ChangeTime(int value)
16241 {
16242   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16243
16244   *time += value;
16245
16246   /* EMC game engine uses value from time counter of RND game engine */
16247   level.native_em_level->lev->time = *time;
16248
16249   DrawGameValue_Time(*time);
16250 }
16251
16252 void RaiseScore(int value)
16253 {
16254   /* EMC game engine and RND game engine have separate score counters */
16255   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16256                 &level.native_em_level->lev->score : &local_player->score);
16257
16258   *score += value;
16259
16260   DrawGameValue_Score(*score);
16261 }
16262 #endif
16263
16264 void RaiseScore(int value)
16265 {
16266   local_player->score += value;
16267
16268 #if 1
16269   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16270
16271   DisplayGameControlValues();
16272 #else
16273   DrawGameValue_Score(local_player->score);
16274 #endif
16275 }
16276
16277 void RaiseScoreElement(int element)
16278 {
16279   switch (element)
16280   {
16281     case EL_EMERALD:
16282     case EL_BD_DIAMOND:
16283     case EL_EMERALD_YELLOW:
16284     case EL_EMERALD_RED:
16285     case EL_EMERALD_PURPLE:
16286     case EL_SP_INFOTRON:
16287       RaiseScore(level.score[SC_EMERALD]);
16288       break;
16289     case EL_DIAMOND:
16290       RaiseScore(level.score[SC_DIAMOND]);
16291       break;
16292     case EL_CRYSTAL:
16293       RaiseScore(level.score[SC_CRYSTAL]);
16294       break;
16295     case EL_PEARL:
16296       RaiseScore(level.score[SC_PEARL]);
16297       break;
16298     case EL_BUG:
16299     case EL_BD_BUTTERFLY:
16300     case EL_SP_ELECTRON:
16301       RaiseScore(level.score[SC_BUG]);
16302       break;
16303     case EL_SPACESHIP:
16304     case EL_BD_FIREFLY:
16305     case EL_SP_SNIKSNAK:
16306       RaiseScore(level.score[SC_SPACESHIP]);
16307       break;
16308     case EL_YAMYAM:
16309     case EL_DARK_YAMYAM:
16310       RaiseScore(level.score[SC_YAMYAM]);
16311       break;
16312     case EL_ROBOT:
16313       RaiseScore(level.score[SC_ROBOT]);
16314       break;
16315     case EL_PACMAN:
16316       RaiseScore(level.score[SC_PACMAN]);
16317       break;
16318     case EL_NUT:
16319       RaiseScore(level.score[SC_NUT]);
16320       break;
16321     case EL_DYNAMITE:
16322     case EL_EM_DYNAMITE:
16323     case EL_SP_DISK_RED:
16324     case EL_DYNABOMB_INCREASE_NUMBER:
16325     case EL_DYNABOMB_INCREASE_SIZE:
16326     case EL_DYNABOMB_INCREASE_POWER:
16327       RaiseScore(level.score[SC_DYNAMITE]);
16328       break;
16329     case EL_SHIELD_NORMAL:
16330     case EL_SHIELD_DEADLY:
16331       RaiseScore(level.score[SC_SHIELD]);
16332       break;
16333     case EL_EXTRA_TIME:
16334       RaiseScore(level.extra_time_score);
16335       break;
16336     case EL_KEY_1:
16337     case EL_KEY_2:
16338     case EL_KEY_3:
16339     case EL_KEY_4:
16340     case EL_EM_KEY_1:
16341     case EL_EM_KEY_2:
16342     case EL_EM_KEY_3:
16343     case EL_EM_KEY_4:
16344     case EL_EMC_KEY_5:
16345     case EL_EMC_KEY_6:
16346     case EL_EMC_KEY_7:
16347     case EL_EMC_KEY_8:
16348     case EL_DC_KEY_WHITE:
16349       RaiseScore(level.score[SC_KEY]);
16350       break;
16351     default:
16352       RaiseScore(element_info[element].collect_score);
16353       break;
16354   }
16355 }
16356
16357 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16358 {
16359   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16360   {
16361 #if defined(NETWORK_AVALIABLE)
16362     if (options.network)
16363       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16364     else
16365 #endif
16366     {
16367       if (quick_quit)
16368       {
16369 #if 1
16370
16371 #if 1
16372         FadeSkipNextFadeIn();
16373 #else
16374         fading = fading_none;
16375 #endif
16376
16377 #else
16378         OpenDoor(DOOR_CLOSE_1);
16379 #endif
16380
16381         game_status = GAME_MODE_MAIN;
16382
16383 #if 1
16384         DrawAndFadeInMainMenu(REDRAW_FIELD);
16385 #else
16386         DrawMainMenu();
16387 #endif
16388       }
16389       else
16390       {
16391 #if 0
16392         FadeOut(REDRAW_FIELD);
16393 #endif
16394
16395         game_status = GAME_MODE_MAIN;
16396
16397         DrawAndFadeInMainMenu(REDRAW_FIELD);
16398       }
16399     }
16400   }
16401   else          /* continue playing the game */
16402   {
16403     if (tape.playing && tape.deactivate_display)
16404       TapeDeactivateDisplayOff(TRUE);
16405
16406     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16407
16408     if (tape.playing && tape.deactivate_display)
16409       TapeDeactivateDisplayOn();
16410   }
16411 }
16412
16413 void RequestQuitGame(boolean ask_if_really_quit)
16414 {
16415   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16416   boolean skip_request = AllPlayersGone || quick_quit;
16417
16418   RequestQuitGameExt(skip_request, quick_quit,
16419                      "Do you really want to quit the game ?");
16420 }
16421
16422
16423 /* ------------------------------------------------------------------------- */
16424 /* random generator functions                                                */
16425 /* ------------------------------------------------------------------------- */
16426
16427 unsigned int InitEngineRandom_RND(long seed)
16428 {
16429   game.num_random_calls = 0;
16430
16431 #if 0
16432   unsigned int rnd_seed = InitEngineRandom(seed);
16433
16434   printf("::: START RND: %d\n", rnd_seed);
16435
16436   return rnd_seed;
16437 #else
16438
16439   return InitEngineRandom(seed);
16440
16441 #endif
16442
16443 }
16444
16445 unsigned int RND(int max)
16446 {
16447   if (max > 0)
16448   {
16449     game.num_random_calls++;
16450
16451     return GetEngineRandom(max);
16452   }
16453
16454   return 0;
16455 }
16456
16457
16458 /* ------------------------------------------------------------------------- */
16459 /* game engine snapshot handling functions                                   */
16460 /* ------------------------------------------------------------------------- */
16461
16462 struct EngineSnapshotInfo
16463 {
16464   /* runtime values for custom element collect score */
16465   int collect_score[NUM_CUSTOM_ELEMENTS];
16466
16467   /* runtime values for group element choice position */
16468   int choice_pos[NUM_GROUP_ELEMENTS];
16469
16470   /* runtime values for belt position animations */
16471   int belt_graphic[4][NUM_BELT_PARTS];
16472   int belt_anim_mode[4][NUM_BELT_PARTS];
16473 };
16474
16475 static struct EngineSnapshotInfo engine_snapshot_rnd;
16476 static char *snapshot_level_identifier = NULL;
16477 static int snapshot_level_nr = -1;
16478
16479 static void SaveEngineSnapshotValues_RND()
16480 {
16481   static int belt_base_active_element[4] =
16482   {
16483     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16484     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16485     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16486     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16487   };
16488   int i, j;
16489
16490   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16491   {
16492     int element = EL_CUSTOM_START + i;
16493
16494     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16495   }
16496
16497   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16498   {
16499     int element = EL_GROUP_START + i;
16500
16501     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16502   }
16503
16504   for (i = 0; i < 4; i++)
16505   {
16506     for (j = 0; j < NUM_BELT_PARTS; j++)
16507     {
16508       int element = belt_base_active_element[i] + j;
16509       int graphic = el2img(element);
16510       int anim_mode = graphic_info[graphic].anim_mode;
16511
16512       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16513       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16514     }
16515   }
16516 }
16517
16518 static void LoadEngineSnapshotValues_RND()
16519 {
16520   unsigned long num_random_calls = game.num_random_calls;
16521   int i, j;
16522
16523   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16524   {
16525     int element = EL_CUSTOM_START + i;
16526
16527     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16528   }
16529
16530   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16531   {
16532     int element = EL_GROUP_START + i;
16533
16534     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16535   }
16536
16537   for (i = 0; i < 4; i++)
16538   {
16539     for (j = 0; j < NUM_BELT_PARTS; j++)
16540     {
16541       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16542       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16543
16544       graphic_info[graphic].anim_mode = anim_mode;
16545     }
16546   }
16547
16548   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16549   {
16550     InitRND(tape.random_seed);
16551     for (i = 0; i < num_random_calls; i++)
16552       RND(1);
16553   }
16554
16555   if (game.num_random_calls != num_random_calls)
16556   {
16557     Error(ERR_INFO, "number of random calls out of sync");
16558     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16559     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16560     Error(ERR_EXIT, "this should not happen -- please debug");
16561   }
16562 }
16563
16564 void SaveEngineSnapshot()
16565 {
16566   /* do not save snapshots from editor */
16567   if (level_editor_test_game)
16568     return;
16569
16570   /* free previous snapshot buffers, if needed */
16571   FreeEngineSnapshotBuffers();
16572
16573   /* copy some special values to a structure better suited for the snapshot */
16574
16575   SaveEngineSnapshotValues_RND();
16576   SaveEngineSnapshotValues_EM();
16577   SaveEngineSnapshotValues_SP();
16578
16579   /* save values stored in special snapshot structure */
16580
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16584
16585   /* save further RND engine values */
16586
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16590
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16595
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16601
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16605
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16607
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16609
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16612
16613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16631
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16634
16635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16638
16639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16641
16642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16645   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16647
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16650
16651   /* save level identification information */
16652
16653   setString(&snapshot_level_identifier, leveldir_current->identifier);
16654   snapshot_level_nr = level_nr;
16655
16656 #if 0
16657   ListNode *node = engine_snapshot_list_rnd;
16658   int num_bytes = 0;
16659
16660   while (node != NULL)
16661   {
16662     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16663
16664     node = node->next;
16665   }
16666
16667   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16668 #endif
16669 }
16670
16671 void LoadEngineSnapshot()
16672 {
16673   /* restore generically stored snapshot buffers */
16674
16675   LoadEngineSnapshotBuffers();
16676
16677   /* restore special values from snapshot structure */
16678
16679   LoadEngineSnapshotValues_RND();
16680   LoadEngineSnapshotValues_EM();
16681   LoadEngineSnapshotValues_SP();
16682 }
16683
16684 boolean CheckEngineSnapshot()
16685 {
16686   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16687           snapshot_level_nr == level_nr);
16688 }
16689
16690
16691 /* ---------- new game button stuff ---------------------------------------- */
16692
16693 /* graphic position values for game buttons */
16694 #define GAME_BUTTON_XSIZE       30
16695 #define GAME_BUTTON_YSIZE       30
16696 #define GAME_BUTTON_XPOS        5
16697 #define GAME_BUTTON_YPOS        215
16698 #define SOUND_BUTTON_XPOS       5
16699 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16700
16701 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16702 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16703 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16704 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16705 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16706 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16707
16708 static struct
16709 {
16710   int *x, *y;
16711   int gd_x, gd_y;
16712   int gadget_id;
16713   char *infotext;
16714 } gamebutton_info[NUM_GAME_BUTTONS] =
16715 {
16716 #if 1
16717   {
16718     &game.button.stop.x,        &game.button.stop.y,
16719     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16720     GAME_CTRL_ID_STOP,
16721     "stop game"
16722   },
16723   {
16724     &game.button.pause.x,       &game.button.pause.y,
16725     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16726     GAME_CTRL_ID_PAUSE,
16727     "pause game"
16728   },
16729   {
16730     &game.button.play.x,        &game.button.play.y,
16731     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16732     GAME_CTRL_ID_PLAY,
16733     "play game"
16734   },
16735   {
16736     &game.button.sound_music.x, &game.button.sound_music.y,
16737     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16738     SOUND_CTRL_ID_MUSIC,
16739     "background music on/off"
16740   },
16741   {
16742     &game.button.sound_loops.x, &game.button.sound_loops.y,
16743     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16744     SOUND_CTRL_ID_LOOPS,
16745     "sound loops on/off"
16746   },
16747   {
16748     &game.button.sound_simple.x,&game.button.sound_simple.y,
16749     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16750     SOUND_CTRL_ID_SIMPLE,
16751     "normal sounds on/off"
16752   }
16753 #else
16754   {
16755     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16756     GAME_CTRL_ID_STOP,
16757     "stop game"
16758   },
16759   {
16760     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16761     GAME_CTRL_ID_PAUSE,
16762     "pause game"
16763   },
16764   {
16765     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16766     GAME_CTRL_ID_PLAY,
16767     "play game"
16768   },
16769   {
16770     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16771     SOUND_CTRL_ID_MUSIC,
16772     "background music on/off"
16773   },
16774   {
16775     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16776     SOUND_CTRL_ID_LOOPS,
16777     "sound loops on/off"
16778   },
16779   {
16780     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16781     SOUND_CTRL_ID_SIMPLE,
16782     "normal sounds on/off"
16783   }
16784 #endif
16785 };
16786
16787 void CreateGameButtons()
16788 {
16789   int i;
16790
16791   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16792   {
16793     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16794     struct GadgetInfo *gi;
16795     int button_type;
16796     boolean checked;
16797     unsigned long event_mask;
16798     int x, y;
16799     int gd_xoffset, gd_yoffset;
16800     int gd_x1, gd_x2, gd_y1, gd_y2;
16801     int id = i;
16802
16803     x = DX + *gamebutton_info[i].x;
16804     y = DY + *gamebutton_info[i].y;
16805     gd_xoffset = gamebutton_info[i].gd_x;
16806     gd_yoffset = gamebutton_info[i].gd_y;
16807     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16808     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16809
16810     if (id == GAME_CTRL_ID_STOP ||
16811         id == GAME_CTRL_ID_PAUSE ||
16812         id == GAME_CTRL_ID_PLAY)
16813     {
16814       button_type = GD_TYPE_NORMAL_BUTTON;
16815       checked = FALSE;
16816       event_mask = GD_EVENT_RELEASED;
16817       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16818       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16819     }
16820     else
16821     {
16822       button_type = GD_TYPE_CHECK_BUTTON;
16823       checked =
16824         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16825          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16826          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16827       event_mask = GD_EVENT_PRESSED;
16828       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16829       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16830     }
16831
16832     gi = CreateGadget(GDI_CUSTOM_ID, id,
16833                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16834 #if 1
16835                       GDI_X, x,
16836                       GDI_Y, y,
16837 #else
16838                       GDI_X, DX + gd_xoffset,
16839                       GDI_Y, DY + gd_yoffset,
16840 #endif
16841                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16842                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16843                       GDI_TYPE, button_type,
16844                       GDI_STATE, GD_BUTTON_UNPRESSED,
16845                       GDI_CHECKED, checked,
16846                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16847                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16848                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16849                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16850                       GDI_DIRECT_DRAW, FALSE,
16851                       GDI_EVENT_MASK, event_mask,
16852                       GDI_CALLBACK_ACTION, HandleGameButtons,
16853                       GDI_END);
16854
16855     if (gi == NULL)
16856       Error(ERR_EXIT, "cannot create gadget");
16857
16858     game_gadget[id] = gi;
16859   }
16860 }
16861
16862 void FreeGameButtons()
16863 {
16864   int i;
16865
16866   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16867     FreeGadget(game_gadget[i]);
16868 }
16869
16870 static void MapGameButtons()
16871 {
16872   int i;
16873
16874   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16875     MapGadget(game_gadget[i]);
16876 }
16877
16878 void UnmapGameButtons()
16879 {
16880   int i;
16881
16882   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16883     UnmapGadget(game_gadget[i]);
16884 }
16885
16886 void RedrawGameButtons()
16887 {
16888   int i;
16889
16890   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16891     RedrawGadget(game_gadget[i]);
16892 }
16893
16894 static void HandleGameButtonsExt(int id)
16895 {
16896   if (game_status != GAME_MODE_PLAYING)
16897     return;
16898
16899   switch (id)
16900   {
16901     case GAME_CTRL_ID_STOP:
16902       if (tape.playing)
16903         TapeStop();
16904       else
16905         RequestQuitGame(TRUE);
16906       break;
16907
16908     case GAME_CTRL_ID_PAUSE:
16909       if (options.network)
16910       {
16911 #if defined(NETWORK_AVALIABLE)
16912         if (tape.pausing)
16913           SendToServer_ContinuePlaying();
16914         else
16915           SendToServer_PausePlaying();
16916 #endif
16917       }
16918       else
16919         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16920       break;
16921
16922     case GAME_CTRL_ID_PLAY:
16923       if (tape.pausing)
16924       {
16925 #if defined(NETWORK_AVALIABLE)
16926         if (options.network)
16927           SendToServer_ContinuePlaying();
16928         else
16929 #endif
16930         {
16931           tape.pausing = FALSE;
16932           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16933         }
16934       }
16935       break;
16936
16937     case SOUND_CTRL_ID_MUSIC:
16938       if (setup.sound_music)
16939       { 
16940         setup.sound_music = FALSE;
16941
16942         FadeMusic();
16943       }
16944       else if (audio.music_available)
16945       { 
16946         setup.sound = setup.sound_music = TRUE;
16947
16948         SetAudioMode(setup.sound);
16949
16950         PlayLevelMusic();
16951       }
16952       break;
16953
16954     case SOUND_CTRL_ID_LOOPS:
16955       if (setup.sound_loops)
16956         setup.sound_loops = FALSE;
16957       else if (audio.loops_available)
16958       {
16959         setup.sound = setup.sound_loops = TRUE;
16960
16961         SetAudioMode(setup.sound);
16962       }
16963       break;
16964
16965     case SOUND_CTRL_ID_SIMPLE:
16966       if (setup.sound_simple)
16967         setup.sound_simple = FALSE;
16968       else if (audio.sound_available)
16969       {
16970         setup.sound = setup.sound_simple = TRUE;
16971
16972         SetAudioMode(setup.sound);
16973       }
16974       break;
16975
16976     default:
16977       break;
16978   }
16979 }
16980
16981 static void HandleGameButtons(struct GadgetInfo *gi)
16982 {
16983   HandleGameButtonsExt(gi->custom_id);
16984 }
16985
16986 void HandleSoundButtonKeys(Key key)
16987 {
16988 #if 1
16989   if (key == setup.shortcut.sound_simple)
16990     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16991   else if (key == setup.shortcut.sound_loops)
16992     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16993   else if (key == setup.shortcut.sound_music)
16994     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16995 #else
16996   if (key == setup.shortcut.sound_simple)
16997     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16998   else if (key == setup.shortcut.sound_loops)
16999     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17000   else if (key == setup.shortcut.sound_music)
17001     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17002 #endif
17003 }