rnd-20100713-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 #if 1
2543   /* !!! CHECK AGAIN !!! */
2544   SetPanelBackground();
2545   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2547 #else
2548   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2550 #endif
2551
2552   /* redraw game control buttons */
2553 #if 1
2554   RedrawGameButtons();
2555 #else
2556   UnmapGameButtons();
2557   MapGameButtons();
2558 #endif
2559
2560   game_status = GAME_MODE_PSEUDO_PANEL;
2561
2562 #if 1
2563   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2564 #else
2565   for (i = 0; game_panel_controls[i].nr != -1; i++)
2566 #endif
2567   {
2568 #if 1
2569     int nr = game_panel_order[i].nr;
2570     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2571 #else
2572     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2573     int nr = gpc->nr;
2574 #endif
2575     struct TextPosInfo *pos = gpc->pos;
2576     int type = gpc->type;
2577     int value = gpc->value;
2578     int frame = gpc->frame;
2579 #if 0
2580     int last_value = gpc->last_value;
2581     int last_frame = gpc->last_frame;
2582 #endif
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591 #if 0
2592     if (value == last_value && frame == last_frame)
2593       continue;
2594 #endif
2595
2596     gpc->last_value = value;
2597     gpc->last_frame = frame;
2598
2599 #if 0
2600     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2601 #endif
2602
2603     if (type == TYPE_INTEGER)
2604     {
2605       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606           nr == GAME_PANEL_TIME)
2607       {
2608         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2609
2610         if (use_dynamic_size)           /* use dynamic number of digits */
2611         {
2612           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614           int size2 = size1 + 1;
2615           int font1 = pos->font;
2616           int font2 = pos->font_alt;
2617
2618           size = (value < value_change ? size1 : size2);
2619           font = (value < value_change ? font1 : font2);
2620
2621 #if 0
2622           /* clear background if value just changed its size (dynamic digits) */
2623           if ((last_value < value_change) != (value < value_change))
2624           {
2625             int width1 = size1 * getFontWidth(font1);
2626             int width2 = size2 * getFontWidth(font2);
2627             int max_width = MAX(width1, width2);
2628             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2629
2630             pos->width = max_width;
2631
2632             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633                                        max_width, max_height);
2634           }
2635 #endif
2636         }
2637       }
2638
2639 #if 1
2640       /* correct text size if "digits" is zero or less */
2641       if (size <= 0)
2642         size = strlen(int2str(value, size));
2643
2644       /* dynamically correct text alignment */
2645       pos->width = size * getFontWidth(font);
2646 #endif
2647
2648       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649                   int2str(value, size), font, mask_mode);
2650     }
2651     else if (type == TYPE_ELEMENT)
2652     {
2653       int element, graphic;
2654       Bitmap *src_bitmap;
2655       int src_x, src_y;
2656       int width, height;
2657       int dst_x = PANEL_XPOS(pos);
2658       int dst_y = PANEL_YPOS(pos);
2659
2660 #if 1
2661       if (value != EL_UNDEFINED && value != EL_EMPTY)
2662       {
2663         element = value;
2664         graphic = el2panelimg(value);
2665
2666         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2667
2668 #if 1
2669         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2670           size = TILESIZE;
2671 #endif
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2674                               &src_x, &src_y);
2675
2676         width  = graphic_info[graphic].width  * size / TILESIZE;
2677         height = graphic_info[graphic].height * size / TILESIZE;
2678
2679         if (draw_masked)
2680         {
2681           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682                         dst_x - src_x, dst_y - src_y);
2683           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2684                            dst_x, dst_y);
2685         }
2686         else
2687         {
2688           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2689                      dst_x, dst_y);
2690         }
2691       }
2692 #else
2693       if (value == EL_UNDEFINED || value == EL_EMPTY)
2694       {
2695         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696         graphic = el2panelimg(element);
2697
2698         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2701       }
2702       else
2703       {
2704         element = value;
2705         graphic = el2panelimg(value);
2706
2707         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2708       }
2709
2710       width  = graphic_info[graphic].width  * size / TILESIZE;
2711       height = graphic_info[graphic].height * size / TILESIZE;
2712
2713       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2714 #endif
2715     }
2716     else if (type == TYPE_STRING)
2717     {
2718       boolean active = (value != 0);
2719       char *state_normal = "off";
2720       char *state_active = "on";
2721       char *state = (active ? state_active : state_normal);
2722       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2724                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2725                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2726
2727       if (nr == GAME_PANEL_GRAVITY_STATE)
2728       {
2729         int font1 = pos->font;          /* (used for normal state) */
2730         int font2 = pos->font_alt;      /* (used for active state) */
2731 #if 0
2732         int size1 = strlen(state_normal);
2733         int size2 = strlen(state_active);
2734         int width1 = size1 * getFontWidth(font1);
2735         int width2 = size2 * getFontWidth(font2);
2736         int max_width = MAX(width1, width2);
2737         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2738
2739         pos->width = max_width;
2740
2741         /* clear background for values that may have changed its size */
2742         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743                                    max_width, max_height);
2744 #endif
2745
2746         font = (active ? font2 : font1);
2747       }
2748
2749       if (s != NULL)
2750       {
2751         char *s_cut;
2752
2753 #if 1
2754         if (size <= 0)
2755         {
2756           /* don't truncate output if "chars" is zero or less */
2757           size = strlen(s);
2758
2759           /* dynamically correct text alignment */
2760           pos->width = size * getFontWidth(font);
2761         }
2762 #endif
2763
2764         s_cut = getStringCopyN(s, size);
2765
2766         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                     s_cut, font, mask_mode);
2768
2769         free(s_cut);
2770       }
2771     }
2772
2773     redraw_mask |= REDRAW_DOOR_1;
2774   }
2775
2776   game_status = GAME_MODE_PLAYING;
2777 }
2778
2779 void UpdateAndDisplayGameControlValues()
2780 {
2781   if (tape.warp_forward)
2782     return;
2783
2784   UpdateGameControlValues();
2785   DisplayGameControlValues();
2786 }
2787
2788 void DrawGameValue_Emeralds(int value)
2789 {
2790   struct TextPosInfo *pos = &game.panel.gems;
2791 #if 1
2792   int font_nr = pos->font;
2793 #else
2794   int font_nr = FONT_TEXT_2;
2795 #endif
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Dynamite(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.inventory_count;
2814 #if 1
2815   int font_nr = pos->font;
2816 #else
2817   int font_nr = FONT_TEXT_2;
2818 #endif
2819   int font_width = getFontWidth(font_nr);
2820   int chars = pos->size;
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   pos->width = chars * font_width;
2830
2831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2832 }
2833
2834 void DrawGameValue_Score(int value)
2835 {
2836   struct TextPosInfo *pos = &game.panel.score;
2837 #if 1
2838   int font_nr = pos->font;
2839 #else
2840   int font_nr = FONT_TEXT_2;
2841 #endif
2842   int font_width = getFontWidth(font_nr);
2843   int chars = pos->size;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849   if (PANEL_DEACTIVATED(pos))
2850     return;
2851
2852   pos->width = chars * font_width;
2853
2854   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2855 }
2856
2857 void DrawGameValue_Time(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.time;
2860   static int last_value = -1;
2861   int chars1 = 3;
2862   int chars2 = 4;
2863   int chars = pos->size;
2864 #if 1
2865   int font1_nr = pos->font;
2866   int font2_nr = pos->font_alt;
2867 #else
2868   int font1_nr = FONT_TEXT_2;
2869   int font2_nr = FONT_TEXT_1;
2870 #endif
2871   int font_nr = font1_nr;
2872   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2873
2874 #if 1
2875   return;       /* !!! USE NEW STUFF !!! */
2876 #endif
2877
2878   if (PANEL_DEACTIVATED(pos))
2879     return;
2880
2881   if (use_dynamic_chars)                /* use dynamic number of chars */
2882   {
2883     chars   = (value < 1000 ? chars1   : chars2);
2884     font_nr = (value < 1000 ? font1_nr : font2_nr);
2885   }
2886
2887   /* clear background if value just changed its size (dynamic chars only) */
2888   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2889   {
2890     int width1 = chars1 * getFontWidth(font1_nr);
2891     int width2 = chars2 * getFontWidth(font2_nr);
2892     int max_width = MAX(width1, width2);
2893     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2894
2895     pos->width = max_width;
2896
2897     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898                                max_width, max_height);
2899   }
2900
2901   pos->width = chars * getFontWidth(font_nr);
2902
2903   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2904
2905   last_value = value;
2906 }
2907
2908 void DrawGameValue_Level(int value)
2909 {
2910   struct TextPosInfo *pos = &game.panel.level_number;
2911   int chars1 = 2;
2912   int chars2 = 3;
2913   int chars = pos->size;
2914 #if 1
2915   int font1_nr = pos->font;
2916   int font2_nr = pos->font_alt;
2917 #else
2918   int font1_nr = FONT_TEXT_2;
2919   int font2_nr = FONT_TEXT_1;
2920 #endif
2921   int font_nr = font1_nr;
2922   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2923
2924 #if 1
2925   return;       /* !!! USE NEW STUFF !!! */
2926 #endif
2927
2928   if (PANEL_DEACTIVATED(pos))
2929     return;
2930
2931   if (use_dynamic_chars)                /* use dynamic number of chars */
2932   {
2933     chars   = (level_nr < 100 ? chars1   : chars2);
2934     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2935   }
2936
2937   pos->width = chars * getFontWidth(font_nr);
2938
2939   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2940 }
2941
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2943 {
2944 #if 0
2945   struct TextPosInfo *pos = &game.panel.keys;
2946 #endif
2947 #if 0
2948   int base_key_graphic = EL_KEY_1;
2949 #endif
2950   int i;
2951
2952 #if 1
2953   return;       /* !!! USE NEW STUFF !!! */
2954 #endif
2955
2956 #if 0
2957   if (PANEL_DEACTIVATED(pos))
2958     return;
2959 #endif
2960
2961 #if 0
2962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963     base_key_graphic = EL_EM_KEY_1;
2964 #endif
2965
2966 #if 0
2967   pos->width = 4 * MINI_TILEX;
2968 #endif
2969
2970 #if 1
2971   for (i = 0; i < MAX_NUM_KEYS; i++)
2972 #else
2973   /* currently only 4 of 8 possible keys are displayed */
2974   for (i = 0; i < STD_NUM_KEYS; i++)
2975 #endif
2976   {
2977 #if 1
2978     struct TextPosInfo *pos = &game.panel.key[i];
2979 #endif
2980     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981     int src_y = DOOR_GFX_PAGEY1 + 123;
2982 #if 1
2983     int dst_x = PANEL_XPOS(pos);
2984     int dst_y = PANEL_YPOS(pos);
2985 #else
2986     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987     int dst_y = PANEL_YPOS(pos);
2988 #endif
2989
2990 #if 1
2991     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2993                    EL_KEY_1) + i;
2994     int graphic = el2edimg(element);
2995 #endif
2996
2997 #if 1
2998     if (PANEL_DEACTIVATED(pos))
2999       continue;
3000 #endif
3001
3002 #if 0
3003     /* masked blit with tiles from half-size scaled bitmap does not work yet
3004        (no mask bitmap created for these sizes after loading and scaling) --
3005        solution: load without creating mask, scale, then create final mask */
3006
3007     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3009
3010     if (key[i])
3011     {
3012 #if 0
3013       int graphic = el2edimg(base_key_graphic + i);
3014 #endif
3015       Bitmap *src_bitmap;
3016       int src_x, src_y;
3017
3018       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3019
3020       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021                     dst_x - src_x, dst_y - src_y);
3022       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3023                        dst_x, dst_y);
3024     }
3025 #else
3026 #if 1
3027     if (key[i])
3028       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3029     else
3030       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3032 #else
3033     if (key[i])
3034       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3035     else
3036       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3038 #endif
3039 #endif
3040   }
3041 }
3042
3043 #else
3044
3045 void DrawGameValue_Emeralds(int value)
3046 {
3047   int font_nr = FONT_TEXT_2;
3048   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3049
3050   if (PANEL_DEACTIVATED(game.panel.gems))
3051     return;
3052
3053   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3054 }
3055
3056 void DrawGameValue_Dynamite(int value)
3057 {
3058   int font_nr = FONT_TEXT_2;
3059   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060
3061   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3062     return;
3063
3064   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3065 }
3066
3067 void DrawGameValue_Score(int value)
3068 {
3069   int font_nr = FONT_TEXT_2;
3070   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3071
3072   if (PANEL_DEACTIVATED(game.panel.score))
3073     return;
3074
3075   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3076 }
3077
3078 void DrawGameValue_Time(int value)
3079 {
3080   int font1_nr = FONT_TEXT_2;
3081 #if 1
3082   int font2_nr = FONT_TEXT_1;
3083 #else
3084   int font2_nr = FONT_LEVEL_NUMBER;
3085 #endif
3086   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3088
3089   if (PANEL_DEACTIVATED(game.panel.time))
3090     return;
3091
3092   /* clear background if value just changed its size */
3093   if (value == 999 || value == 1000)
3094     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3095
3096   if (value < 1000)
3097     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3098   else
3099     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3100 }
3101
3102 void DrawGameValue_Level(int value)
3103 {
3104   int font1_nr = FONT_TEXT_2;
3105 #if 1
3106   int font2_nr = FONT_TEXT_1;
3107 #else
3108   int font2_nr = FONT_LEVEL_NUMBER;
3109 #endif
3110
3111   if (PANEL_DEACTIVATED(game.panel.level))
3112     return;
3113
3114   if (level_nr < 100)
3115     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3116   else
3117     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3118 }
3119
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3121 {
3122   int base_key_graphic = EL_KEY_1;
3123   int i;
3124
3125   if (PANEL_DEACTIVATED(game.panel.keys))
3126     return;
3127
3128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129     base_key_graphic = EL_EM_KEY_1;
3130
3131   /* currently only 4 of 8 possible keys are displayed */
3132   for (i = 0; i < STD_NUM_KEYS; i++)
3133   {
3134     int x = XX_KEYS + i * MINI_TILEX;
3135     int y = YY_KEYS;
3136
3137     if (key[i])
3138       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3139     else
3140       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3142   }
3143 }
3144
3145 #endif
3146
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3148                        int key_bits)
3149 {
3150   int key[MAX_NUM_KEYS];
3151   int i;
3152
3153   /* prevent EM engine from updating time/score values parallel to GameWon() */
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155       local_player->LevelSolved)
3156     return;
3157
3158   for (i = 0; i < MAX_NUM_KEYS; i++)
3159     key[i] = key_bits & (1 << i);
3160
3161   DrawGameValue_Level(level_nr);
3162
3163   DrawGameValue_Emeralds(emeralds);
3164   DrawGameValue_Dynamite(dynamite);
3165   DrawGameValue_Score(score);
3166   DrawGameValue_Time(time);
3167
3168   DrawGameValue_Keys(key);
3169 }
3170
3171 void UpdateGameDoorValues()
3172 {
3173   UpdateGameControlValues();
3174 }
3175
3176 void DrawGameDoorValues()
3177 {
3178   DisplayGameControlValues();
3179 }
3180
3181 void DrawGameDoorValues_OLD()
3182 {
3183   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184   int dynamite_value = 0;
3185   int score_value = (local_player->LevelSolved ? local_player->score_final :
3186                      local_player->score);
3187   int gems_value = local_player->gems_still_needed;
3188   int key_bits = 0;
3189   int i, j;
3190
3191   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3192   {
3193     DrawGameDoorValues_EM();
3194
3195     return;
3196   }
3197
3198   if (game.centered_player_nr == -1)
3199   {
3200     for (i = 0; i < MAX_PLAYERS; i++)
3201     {
3202       for (j = 0; j < MAX_NUM_KEYS; j++)
3203         if (stored_player[i].key[j])
3204           key_bits |= (1 << j);
3205
3206       dynamite_value += stored_player[i].inventory_size;
3207     }
3208   }
3209   else
3210   {
3211     int player_nr = game.centered_player_nr;
3212
3213     for (i = 0; i < MAX_NUM_KEYS; i++)
3214       if (stored_player[player_nr].key[i])
3215         key_bits |= (1 << i);
3216
3217     dynamite_value = stored_player[player_nr].inventory_size;
3218   }
3219
3220   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3221                     key_bits);
3222 }
3223
3224
3225 /*
3226   =============================================================================
3227   InitGameEngine()
3228   -----------------------------------------------------------------------------
3229   initialize game engine due to level / tape version number
3230   =============================================================================
3231 */
3232
3233 static void InitGameEngine()
3234 {
3235   int i, j, k, l, x, y;
3236
3237   /* set game engine from tape file when re-playing, else from level file */
3238   game.engine_version = (tape.playing ? tape.engine_version :
3239                          level.game_version);
3240
3241   /* ---------------------------------------------------------------------- */
3242   /* set flags for bugs and changes according to active game engine version */
3243   /* ---------------------------------------------------------------------- */
3244
3245   /*
3246     Summary of bugfix/change:
3247     Fixed handling for custom elements that change when pushed by the player.
3248
3249     Fixed/changed in version:
3250     3.1.0
3251
3252     Description:
3253     Before 3.1.0, custom elements that "change when pushing" changed directly
3254     after the player started pushing them (until then handled in "DigField()").
3255     Since 3.1.0, these custom elements are not changed until the "pushing"
3256     move of the element is finished (now handled in "ContinueMoving()").
3257
3258     Affected levels/tapes:
3259     The first condition is generally needed for all levels/tapes before version
3260     3.1.0, which might use the old behaviour before it was changed; known tapes
3261     that are affected are some tapes from the level set "Walpurgis Gardens" by
3262     Jamie Cullen.
3263     The second condition is an exception from the above case and is needed for
3264     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265     above (including some development versions of 3.1.0), but before it was
3266     known that this change would break tapes like the above and was fixed in
3267     3.1.1, so that the changed behaviour was active although the engine version
3268     while recording maybe was before 3.1.0. There is at least one tape that is
3269     affected by this exception, which is the tape for the one-level set "Bug
3270     Machine" by Juergen Bonhagen.
3271   */
3272
3273   game.use_change_when_pushing_bug =
3274     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3275      !(tape.playing &&
3276        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3278
3279   /*
3280     Summary of bugfix/change:
3281     Fixed handling for blocking the field the player leaves when moving.
3282
3283     Fixed/changed in version:
3284     3.1.1
3285
3286     Description:
3287     Before 3.1.1, when "block last field when moving" was enabled, the field
3288     the player is leaving when moving was blocked for the time of the move,
3289     and was directly unblocked afterwards. This resulted in the last field
3290     being blocked for exactly one less than the number of frames of one player
3291     move. Additionally, even when blocking was disabled, the last field was
3292     blocked for exactly one frame.
3293     Since 3.1.1, due to changes in player movement handling, the last field
3294     is not blocked at all when blocking is disabled. When blocking is enabled,
3295     the last field is blocked for exactly the number of frames of one player
3296     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297     last field is blocked for exactly one more than the number of frames of
3298     one player move.
3299
3300     Affected levels/tapes:
3301     (!!! yet to be determined -- probably many !!!)
3302   */
3303
3304   game.use_block_last_field_bug =
3305     (game.engine_version < VERSION_IDENT(3,1,1,0));
3306
3307   /*
3308     Summary of bugfix/change:
3309     Changed behaviour of CE changes with multiple changes per single frame.
3310
3311     Fixed/changed in version:
3312     3.2.0-6
3313
3314     Description:
3315     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316     This resulted in race conditions where CEs seem to behave strange in some
3317     situations (where triggered CE changes were just skipped because there was
3318     already a CE change on that tile in the playfield in that engine frame).
3319     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320     (The number of changes per frame must be limited in any case, because else
3321     it is easily possible to define CE changes that would result in an infinite
3322     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323     should be set large enough so that it would only be reached in cases where
3324     the corresponding CE change conditions run into a loop. Therefore, it seems
3325     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326     maximal number of change pages for custom elements.)
3327
3328     Affected levels/tapes:
3329     Probably many.
3330   */
3331
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333   game.max_num_changes_per_frame = 1;
3334 #else
3335   game.max_num_changes_per_frame =
3336     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3337 #endif
3338
3339   /* ---------------------------------------------------------------------- */
3340
3341   /* default scan direction: scan playfield from top/left to bottom/right */
3342   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3343
3344   /* dynamically adjust element properties according to game engine version */
3345   InitElementPropertiesEngine(game.engine_version);
3346
3347 #if 0
3348   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349   printf("          tape version == %06d [%s] [file: %06d]\n",
3350          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3351          tape.file_version);
3352   printf("       => game.engine_version == %06d\n", game.engine_version);
3353 #endif
3354
3355   /* ---------- initialize player's initial move delay --------------------- */
3356
3357   /* dynamically adjust player properties according to level information */
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359     game.initial_move_delay_value[i] =
3360       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3361
3362   /* dynamically adjust player properties according to game engine version */
3363   for (i = 0; i < MAX_PLAYERS; i++)
3364     game.initial_move_delay[i] =
3365       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366        game.initial_move_delay_value[i] : 0);
3367
3368   /* ---------- initialize player's initial push delay --------------------- */
3369
3370   /* dynamically adjust player properties according to game engine version */
3371   game.initial_push_delay_value =
3372     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3373
3374   /* ---------- initialize changing elements ------------------------------- */
3375
3376   /* initialize changing elements information */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378   {
3379     struct ElementInfo *ei = &element_info[i];
3380
3381     /* this pointer might have been changed in the level editor */
3382     ei->change = &ei->change_page[0];
3383
3384     if (!IS_CUSTOM_ELEMENT(i))
3385     {
3386       ei->change->target_element = EL_EMPTY_SPACE;
3387       ei->change->delay_fixed = 0;
3388       ei->change->delay_random = 0;
3389       ei->change->delay_frames = 1;
3390     }
3391
3392     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3393     {
3394       ei->has_change_event[j] = FALSE;
3395
3396       ei->event_page_nr[j] = 0;
3397       ei->event_page[j] = &ei->change_page[0];
3398     }
3399   }
3400
3401   /* add changing elements from pre-defined list */
3402   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3403   {
3404     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405     struct ElementInfo *ei = &element_info[ch_delay->element];
3406
3407     ei->change->target_element       = ch_delay->target_element;
3408     ei->change->delay_fixed          = ch_delay->change_delay;
3409
3410     ei->change->pre_change_function  = ch_delay->pre_change_function;
3411     ei->change->change_function      = ch_delay->change_function;
3412     ei->change->post_change_function = ch_delay->post_change_function;
3413
3414     ei->change->can_change = TRUE;
3415     ei->change->can_change_or_has_action = TRUE;
3416
3417     ei->has_change_event[CE_DELAY] = TRUE;
3418
3419     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3421   }
3422
3423   /* ---------- initialize internal run-time variables --------------------- */
3424
3425   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3426   {
3427     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3428
3429     for (j = 0; j < ei->num_change_pages; j++)
3430     {
3431       ei->change_page[j].can_change_or_has_action =
3432         (ei->change_page[j].can_change |
3433          ei->change_page[j].has_action);
3434     }
3435   }
3436
3437   /* add change events from custom element configuration */
3438   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3439   {
3440     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3441
3442     for (j = 0; j < ei->num_change_pages; j++)
3443     {
3444       if (!ei->change_page[j].can_change_or_has_action)
3445         continue;
3446
3447       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3448       {
3449         /* only add event page for the first page found with this event */
3450         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3451         {
3452           ei->has_change_event[k] = TRUE;
3453
3454           ei->event_page_nr[k] = j;
3455           ei->event_page[k] = &ei->change_page[j];
3456         }
3457       }
3458     }
3459   }
3460
3461 #if 1
3462   /* ---------- initialize reference elements in change conditions --------- */
3463
3464   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3465   {
3466     int element = EL_CUSTOM_START + i;
3467     struct ElementInfo *ei = &element_info[element];
3468
3469     for (j = 0; j < ei->num_change_pages; j++)
3470     {
3471       int trigger_element = ei->change_page[j].initial_trigger_element;
3472
3473       if (trigger_element >= EL_PREV_CE_8 &&
3474           trigger_element <= EL_NEXT_CE_8)
3475         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3476
3477       ei->change_page[j].trigger_element = trigger_element;
3478     }
3479   }
3480 #endif
3481
3482   /* ---------- initialize run-time trigger player and element ------------- */
3483
3484   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3485   {
3486     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3487
3488     for (j = 0; j < ei->num_change_pages; j++)
3489     {
3490       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494       ei->change_page[j].actual_trigger_ce_value = 0;
3495       ei->change_page[j].actual_trigger_ce_score = 0;
3496     }
3497   }
3498
3499   /* ---------- initialize trigger events ---------------------------------- */
3500
3501   /* initialize trigger events information */
3502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504       trigger_events[i][j] = FALSE;
3505
3506   /* add trigger events from element change event properties */
3507   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3508   {
3509     struct ElementInfo *ei = &element_info[i];
3510
3511     for (j = 0; j < ei->num_change_pages; j++)
3512     {
3513       if (!ei->change_page[j].can_change_or_has_action)
3514         continue;
3515
3516       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3517       {
3518         int trigger_element = ei->change_page[j].trigger_element;
3519
3520         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3521         {
3522           if (ei->change_page[j].has_event[k])
3523           {
3524             if (IS_GROUP_ELEMENT(trigger_element))
3525             {
3526               struct ElementGroupInfo *group =
3527                 element_info[trigger_element].group;
3528
3529               for (l = 0; l < group->num_elements_resolved; l++)
3530                 trigger_events[group->element_resolved[l]][k] = TRUE;
3531             }
3532             else if (trigger_element == EL_ANY_ELEMENT)
3533               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534                 trigger_events[l][k] = TRUE;
3535             else
3536               trigger_events[trigger_element][k] = TRUE;
3537           }
3538         }
3539       }
3540     }
3541   }
3542
3543   /* ---------- initialize push delay -------------------------------------- */
3544
3545   /* initialize push delay values to default */
3546   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3547   {
3548     if (!IS_CUSTOM_ELEMENT(i))
3549     {
3550       /* set default push delay values (corrected since version 3.0.7-1) */
3551       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3552       {
3553         element_info[i].push_delay_fixed = 2;
3554         element_info[i].push_delay_random = 8;
3555       }
3556       else
3557       {
3558         element_info[i].push_delay_fixed = 8;
3559         element_info[i].push_delay_random = 8;
3560       }
3561     }
3562   }
3563
3564   /* set push delay value for certain elements from pre-defined list */
3565   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3566   {
3567     int e = push_delay_list[i].element;
3568
3569     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3570     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3571   }
3572
3573   /* set push delay value for Supaplex elements for newer engine versions */
3574   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3575   {
3576     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577     {
3578       if (IS_SP_ELEMENT(i))
3579       {
3580         /* set SP push delay to just enough to push under a falling zonk */
3581         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3582
3583         element_info[i].push_delay_fixed  = delay;
3584         element_info[i].push_delay_random = 0;
3585       }
3586     }
3587   }
3588
3589   /* ---------- initialize move stepsize ----------------------------------- */
3590
3591   /* initialize move stepsize values to default */
3592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593     if (!IS_CUSTOM_ELEMENT(i))
3594       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3595
3596   /* set move stepsize value for certain elements from pre-defined list */
3597   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3598   {
3599     int e = move_stepsize_list[i].element;
3600
3601     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3602   }
3603
3604   /* ---------- initialize collect score ----------------------------------- */
3605
3606   /* initialize collect score values for custom elements from initial value */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_score = element_info[i].collect_score_initial;
3610
3611   /* ---------- initialize collect count ----------------------------------- */
3612
3613   /* initialize collect count values for non-custom elements */
3614   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615     if (!IS_CUSTOM_ELEMENT(i))
3616       element_info[i].collect_count_initial = 0;
3617
3618   /* add collect count values for all elements from pre-defined list */
3619   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620     element_info[collect_count_list[i].element].collect_count_initial =
3621       collect_count_list[i].count;
3622
3623   /* ---------- initialize access direction -------------------------------- */
3624
3625   /* initialize access direction values to default (access from every side) */
3626   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627     if (!IS_CUSTOM_ELEMENT(i))
3628       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3629
3630   /* set access direction value for certain elements from pre-defined list */
3631   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632     element_info[access_direction_list[i].element].access_direction =
3633       access_direction_list[i].direction;
3634
3635   /* ---------- initialize explosion content ------------------------------- */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637   {
3638     if (IS_CUSTOM_ELEMENT(i))
3639       continue;
3640
3641     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3642     {
3643       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3644
3645       element_info[i].content.e[x][y] =
3646         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648          i == EL_PLAYER_3 ? EL_EMERALD :
3649          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650          i == EL_MOLE ? EL_EMERALD_RED :
3651          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656          i == EL_WALL_EMERALD ? EL_EMERALD :
3657          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662          i == EL_WALL_PEARL ? EL_PEARL :
3663          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3664          EL_EMPTY);
3665     }
3666   }
3667
3668   /* ---------- initialize recursion detection ------------------------------ */
3669   recursion_loop_depth = 0;
3670   recursion_loop_detected = FALSE;
3671   recursion_loop_element = EL_UNDEFINED;
3672
3673   /* ---------- initialize graphics engine ---------------------------------- */
3674   game.scroll_delay_value =
3675     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3677   game.scroll_delay_value =
3678     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3679 }
3680
3681 int get_num_special_action(int element, int action_first, int action_last)
3682 {
3683   int num_special_action = 0;
3684   int i, j;
3685
3686   for (i = action_first; i <= action_last; i++)
3687   {
3688     boolean found = FALSE;
3689
3690     for (j = 0; j < NUM_DIRECTIONS; j++)
3691       if (el_act_dir2img(element, i, j) !=
3692           el_act_dir2img(element, ACTION_DEFAULT, j))
3693         found = TRUE;
3694
3695     if (found)
3696       num_special_action++;
3697     else
3698       break;
3699   }
3700
3701   return num_special_action;
3702 }
3703
3704
3705 /*
3706   =============================================================================
3707   InitGame()
3708   -----------------------------------------------------------------------------
3709   initialize and start new game
3710   =============================================================================
3711 */
3712
3713 void InitGame()
3714 {
3715   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3716   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3717   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3718 #if 0
3719   boolean do_fading = (game_status == GAME_MODE_MAIN);
3720 #endif
3721 #if 1
3722   int initial_move_dir = MV_DOWN;
3723 #else
3724   int initial_move_dir = MV_NONE;
3725 #endif
3726   int i, j, x, y;
3727
3728   game_status = GAME_MODE_PLAYING;
3729
3730 #if 1
3731   /* needed if different viewport properties defined for playing */
3732   ChangeViewportPropertiesIfNeeded();
3733 #endif
3734
3735 #if 1
3736   DrawCompleteVideoDisplay();
3737 #endif
3738
3739   InitGameEngine();
3740   InitGameControlValues();
3741
3742   /* don't play tapes over network */
3743   network_playing = (options.network && !tape.playing);
3744
3745   for (i = 0; i < MAX_PLAYERS; i++)
3746   {
3747     struct PlayerInfo *player = &stored_player[i];
3748
3749     player->index_nr = i;
3750     player->index_bit = (1 << i);
3751     player->element_nr = EL_PLAYER_1 + i;
3752
3753     player->present = FALSE;
3754     player->active = FALSE;
3755     player->mapped = FALSE;
3756
3757     player->killed = FALSE;
3758     player->reanimated = FALSE;
3759
3760     player->action = 0;
3761     player->effective_action = 0;
3762     player->programmed_action = 0;
3763
3764     player->score = 0;
3765     player->score_final = 0;
3766
3767     player->gems_still_needed = level.gems_needed;
3768     player->sokobanfields_still_needed = 0;
3769     player->lights_still_needed = 0;
3770     player->friends_still_needed = 0;
3771
3772     for (j = 0; j < MAX_NUM_KEYS; j++)
3773       player->key[j] = FALSE;
3774
3775     player->num_white_keys = 0;
3776
3777     player->dynabomb_count = 0;
3778     player->dynabomb_size = 1;
3779     player->dynabombs_left = 0;
3780     player->dynabomb_xl = FALSE;
3781
3782     player->MovDir = initial_move_dir;
3783     player->MovPos = 0;
3784     player->GfxPos = 0;
3785     player->GfxDir = initial_move_dir;
3786     player->GfxAction = ACTION_DEFAULT;
3787     player->Frame = 0;
3788     player->StepFrame = 0;
3789
3790     player->initial_element = player->element_nr;
3791     player->artwork_element =
3792       (level.use_artwork_element[i] ? level.artwork_element[i] :
3793        player->element_nr);
3794     player->use_murphy = FALSE;
3795
3796     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3797     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3798
3799     player->gravity = level.initial_player_gravity[i];
3800
3801     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3802
3803     player->actual_frame_counter = 0;
3804
3805     player->step_counter = 0;
3806
3807     player->last_move_dir = initial_move_dir;
3808
3809     player->is_active = FALSE;
3810
3811     player->is_waiting = FALSE;
3812     player->is_moving = FALSE;
3813     player->is_auto_moving = FALSE;
3814     player->is_digging = FALSE;
3815     player->is_snapping = FALSE;
3816     player->is_collecting = FALSE;
3817     player->is_pushing = FALSE;
3818     player->is_switching = FALSE;
3819     player->is_dropping = FALSE;
3820     player->is_dropping_pressed = FALSE;
3821
3822     player->is_bored = FALSE;
3823     player->is_sleeping = FALSE;
3824
3825     player->frame_counter_bored = -1;
3826     player->frame_counter_sleeping = -1;
3827
3828     player->anim_delay_counter = 0;
3829     player->post_delay_counter = 0;
3830
3831     player->dir_waiting = initial_move_dir;
3832     player->action_waiting = ACTION_DEFAULT;
3833     player->last_action_waiting = ACTION_DEFAULT;
3834     player->special_action_bored = ACTION_DEFAULT;
3835     player->special_action_sleeping = ACTION_DEFAULT;
3836
3837     player->switch_x = -1;
3838     player->switch_y = -1;
3839
3840     player->drop_x = -1;
3841     player->drop_y = -1;
3842
3843     player->show_envelope = 0;
3844
3845     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3846
3847     player->push_delay       = -1;      /* initialized when pushing starts */
3848     player->push_delay_value = game.initial_push_delay_value;
3849
3850     player->drop_delay = 0;
3851     player->drop_pressed_delay = 0;
3852
3853     player->last_jx = -1;
3854     player->last_jy = -1;
3855     player->jx = -1;
3856     player->jy = -1;
3857
3858     player->shield_normal_time_left = 0;
3859     player->shield_deadly_time_left = 0;
3860
3861     player->inventory_infinite_element = EL_UNDEFINED;
3862     player->inventory_size = 0;
3863
3864     if (level.use_initial_inventory[i])
3865     {
3866       for (j = 0; j < level.initial_inventory_size[i]; j++)
3867       {
3868         int element = level.initial_inventory_content[i][j];
3869         int collect_count = element_info[element].collect_count_initial;
3870         int k;
3871
3872         if (!IS_CUSTOM_ELEMENT(element))
3873           collect_count = 1;
3874
3875         if (collect_count == 0)
3876           player->inventory_infinite_element = element;
3877         else
3878           for (k = 0; k < collect_count; k++)
3879             if (player->inventory_size < MAX_INVENTORY_SIZE)
3880               player->inventory_element[player->inventory_size++] = element;
3881       }
3882     }
3883
3884     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885     SnapField(player, 0, 0);
3886
3887     player->LevelSolved = FALSE;
3888     player->GameOver = FALSE;
3889
3890     player->LevelSolved_GameWon = FALSE;
3891     player->LevelSolved_GameEnd = FALSE;
3892     player->LevelSolved_PanelOff = FALSE;
3893     player->LevelSolved_SaveTape = FALSE;
3894     player->LevelSolved_SaveScore = FALSE;
3895     player->LevelSolved_CountingTime = 0;
3896     player->LevelSolved_CountingScore = 0;
3897
3898     map_player_action[i] = i;
3899   }
3900
3901   network_player_action_received = FALSE;
3902
3903 #if defined(NETWORK_AVALIABLE)
3904   /* initial null action */
3905   if (network_playing)
3906     SendToServer_MovePlayer(MV_NONE);
3907 #endif
3908
3909   ZX = ZY = -1;
3910   ExitX = ExitY = -1;
3911
3912   FrameCounter = 0;
3913   TimeFrames = 0;
3914   TimePlayed = 0;
3915   TimeLeft = level.time;
3916   TapeTime = 0;
3917
3918   ScreenMovDir = MV_NONE;
3919   ScreenMovPos = 0;
3920   ScreenGfxPos = 0;
3921
3922   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3923
3924   AllPlayersGone = FALSE;
3925
3926   game.yamyam_content_nr = 0;
3927   game.robot_wheel_active = FALSE;
3928   game.magic_wall_active = FALSE;
3929   game.magic_wall_time_left = 0;
3930   game.light_time_left = 0;
3931   game.timegate_time_left = 0;
3932   game.switchgate_pos = 0;
3933   game.wind_direction = level.wind_direction_initial;
3934
3935 #if !USE_PLAYER_GRAVITY
3936   game.gravity = FALSE;
3937   game.explosions_delayed = TRUE;
3938 #endif
3939
3940   game.lenses_time_left = 0;
3941   game.magnify_time_left = 0;
3942
3943   game.ball_state = level.ball_state_initial;
3944   game.ball_content_nr = 0;
3945
3946   game.envelope_active = FALSE;
3947
3948   /* set focus to local player for network games, else to all players */
3949   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950   game.centered_player_nr_next = game.centered_player_nr;
3951   game.set_centered_player = FALSE;
3952
3953   if (network_playing && tape.recording)
3954   {
3955     /* store client dependent player focus when recording network games */
3956     tape.centered_player_nr_next = game.centered_player_nr_next;
3957     tape.set_centered_player = TRUE;
3958   }
3959
3960   for (i = 0; i < NUM_BELTS; i++)
3961   {
3962     game.belt_dir[i] = MV_NONE;
3963     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3964   }
3965
3966   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3968
3969   SCAN_PLAYFIELD(x, y)
3970   {
3971     Feld[x][y] = level.field[x][y];
3972     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973     ChangeDelay[x][y] = 0;
3974     ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976     CustomValue[x][y] = 0;              /* initialized in InitField() */
3977 #endif
3978     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3979     AmoebaNr[x][y] = 0;
3980     WasJustMoving[x][y] = 0;
3981     WasJustFalling[x][y] = 0;
3982     CheckCollision[x][y] = 0;
3983     CheckImpact[x][y] = 0;
3984     Stop[x][y] = FALSE;
3985     Pushed[x][y] = FALSE;
3986
3987     ChangeCount[x][y] = 0;
3988     ChangeEvent[x][y] = -1;
3989
3990     ExplodePhase[x][y] = 0;
3991     ExplodeDelay[x][y] = 0;
3992     ExplodeField[x][y] = EX_TYPE_NONE;
3993
3994     RunnerVisit[x][y] = 0;
3995     PlayerVisit[x][y] = 0;
3996
3997     GfxFrame[x][y] = 0;
3998     GfxRandom[x][y] = INIT_GFX_RANDOM();
3999     GfxElement[x][y] = EL_UNDEFINED;
4000     GfxAction[x][y] = ACTION_DEFAULT;
4001     GfxDir[x][y] = MV_NONE;
4002     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4003   }
4004
4005   SCAN_PLAYFIELD(x, y)
4006   {
4007     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4008       emulate_bd = FALSE;
4009     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4010       emulate_sb = FALSE;
4011     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4012       emulate_sp = FALSE;
4013
4014     InitField(x, y, TRUE);
4015
4016     ResetGfxAnimation(x, y);
4017   }
4018
4019   InitBeltMovement();
4020
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     /* set number of special actions for bored and sleeping animation */
4026     player->num_special_action_bored =
4027       get_num_special_action(player->artwork_element,
4028                              ACTION_BORING_1, ACTION_BORING_LAST);
4029     player->num_special_action_sleeping =
4030       get_num_special_action(player->artwork_element,
4031                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4032   }
4033
4034   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035                     emulate_sb ? EMU_SOKOBAN :
4036                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4037
4038 #if USE_NEW_ALL_SLIPPERY
4039   /* initialize type of slippery elements */
4040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4041   {
4042     if (!IS_CUSTOM_ELEMENT(i))
4043     {
4044       /* default: elements slip down either to the left or right randomly */
4045       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4046
4047       /* SP style elements prefer to slip down on the left side */
4048       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4050
4051       /* BD style elements prefer to slip down on the left side */
4052       if (game.emulation == EMU_BOULDERDASH)
4053         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4054     }
4055   }
4056 #endif
4057
4058   /* initialize explosion and ignition delay */
4059   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4060   {
4061     if (!IS_CUSTOM_ELEMENT(i))
4062     {
4063       int num_phase = 8;
4064       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067       int last_phase = (num_phase + 1) * delay;
4068       int half_phase = (num_phase / 2) * delay;
4069
4070       element_info[i].explosion_delay = last_phase - 1;
4071       element_info[i].ignition_delay = half_phase;
4072
4073       if (i == EL_BLACK_ORB)
4074         element_info[i].ignition_delay = 1;
4075     }
4076
4077 #if 0
4078     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4079       element_info[i].explosion_delay = 1;
4080
4081     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4082       element_info[i].ignition_delay = 1;
4083 #endif
4084   }
4085
4086   /* correct non-moving belts to start moving left */
4087   for (i = 0; i < NUM_BELTS; i++)
4088     if (game.belt_dir[i] == MV_NONE)
4089       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4090
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093   /* choose default local player */
4094   local_player = &stored_player[0];
4095
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097     stored_player[i].connected = FALSE;
4098
4099   local_player->connected = TRUE;
4100   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4101
4102   if (tape.playing)
4103   {
4104     /* try to guess locally connected team mode players (needed for correct
4105        assignment of player figures from level to locally playing players) */
4106
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (tape.player_participates[i])
4109         stored_player[i].connected = TRUE;
4110   }
4111   else if (setup.team_mode && !options.network)
4112   {
4113     /* try to guess locally connected team mode players (needed for correct
4114        assignment of player figures from level to locally playing players) */
4115
4116     for (i = 0; i < MAX_PLAYERS; i++)
4117       if (setup.input[i].use_joystick ||
4118           setup.input[i].key.left != KSYM_UNDEFINED)
4119         stored_player[i].connected = TRUE;
4120   }
4121
4122 #if 0
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124     printf("::: player %d: %s\n", i,
4125            (stored_player[i].connected ? "connected" : "not connected"));
4126
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128     printf("::: player %d: %s\n", i,
4129            (stored_player[i].present ? "present" : "not present"));
4130 #endif
4131
4132   /* check if any connected player was not found in playfield */
4133   for (i = 0; i < MAX_PLAYERS; i++)
4134   {
4135     struct PlayerInfo *player = &stored_player[i];
4136
4137     if (player->connected && !player->present)
4138     {
4139       struct PlayerInfo *field_player = NULL;
4140
4141 #if 0
4142       printf("::: looking for field player for player %d ...\n", i);
4143 #endif
4144
4145       /* assign first free player found that is present in the playfield */
4146
4147       /* first try: look for unmapped playfield player that is not connected */
4148       if (field_player == NULL)
4149         for (j = 0; j < MAX_PLAYERS; j++)
4150           if (stored_player[j].present &&
4151               !stored_player[j].mapped &&
4152               !stored_player[j].connected)
4153             field_player = &stored_player[j];
4154
4155       /* second try: look for *any* unmapped playfield player */
4156       if (field_player == NULL)
4157         for (j = 0; j < MAX_PLAYERS; j++)
4158           if (stored_player[j].present &&
4159               !stored_player[j].mapped)
4160             field_player = &stored_player[j];
4161
4162       if (field_player != NULL)
4163       {
4164         int jx = field_player->jx, jy = field_player->jy;
4165
4166 #if 0
4167         printf("::: found player figure %d\n", field_player->index_nr);
4168 #endif
4169
4170         player->present = FALSE;
4171         player->active = FALSE;
4172
4173         field_player->present = TRUE;
4174         field_player->active = TRUE;
4175
4176         /*
4177         player->initial_element = field_player->initial_element;
4178         player->artwork_element = field_player->artwork_element;
4179
4180         player->block_last_field       = field_player->block_last_field;
4181         player->block_delay_adjustment = field_player->block_delay_adjustment;
4182         */
4183
4184         StorePlayer[jx][jy] = field_player->element_nr;
4185
4186         field_player->jx = field_player->last_jx = jx;
4187         field_player->jy = field_player->last_jy = jy;
4188
4189         if (local_player == player)
4190           local_player = field_player;
4191
4192         map_player_action[field_player->index_nr] = i;
4193
4194         field_player->mapped = TRUE;
4195
4196 #if 0
4197         printf("::: map_player_action[%d] == %d\n",
4198                field_player->index_nr, i);
4199 #endif
4200       }
4201     }
4202
4203     if (player->connected && player->present)
4204       player->mapped = TRUE;
4205   }
4206
4207 #else
4208
4209   /* check if any connected player was not found in playfield */
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211   {
4212     struct PlayerInfo *player = &stored_player[i];
4213
4214     if (player->connected && !player->present)
4215     {
4216       for (j = 0; j < MAX_PLAYERS; j++)
4217       {
4218         struct PlayerInfo *field_player = &stored_player[j];
4219         int jx = field_player->jx, jy = field_player->jy;
4220
4221         /* assign first free player found that is present in the playfield */
4222         if (field_player->present && !field_player->connected)
4223         {
4224           player->present = TRUE;
4225           player->active = TRUE;
4226
4227           field_player->present = FALSE;
4228           field_player->active = FALSE;
4229
4230           player->initial_element = field_player->initial_element;
4231           player->artwork_element = field_player->artwork_element;
4232
4233           player->block_last_field       = field_player->block_last_field;
4234           player->block_delay_adjustment = field_player->block_delay_adjustment;
4235
4236           StorePlayer[jx][jy] = player->element_nr;
4237
4238           player->jx = player->last_jx = jx;
4239           player->jy = player->last_jy = jy;
4240
4241           break;
4242         }
4243       }
4244     }
4245   }
4246 #endif
4247
4248 #if 0
4249   printf("::: local_player->present == %d\n", local_player->present);
4250 #endif
4251
4252   if (tape.playing)
4253   {
4254     /* when playing a tape, eliminate all players who do not participate */
4255
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257     for (i = 0; i < MAX_PLAYERS; i++)
4258     {
4259       if (stored_player[i].active &&
4260           !tape.player_participates[map_player_action[i]])
4261       {
4262         struct PlayerInfo *player = &stored_player[i];
4263         int jx = player->jx, jy = player->jy;
4264
4265         player->active = FALSE;
4266         StorePlayer[jx][jy] = 0;
4267         Feld[jx][jy] = EL_EMPTY;
4268       }
4269     }
4270 #else
4271     for (i = 0; i < MAX_PLAYERS; i++)
4272     {
4273       if (stored_player[i].active &&
4274           !tape.player_participates[i])
4275       {
4276         struct PlayerInfo *player = &stored_player[i];
4277         int jx = player->jx, jy = player->jy;
4278
4279         player->active = FALSE;
4280         StorePlayer[jx][jy] = 0;
4281         Feld[jx][jy] = EL_EMPTY;
4282       }
4283     }
4284 #endif
4285   }
4286   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4287   {
4288     /* when in single player mode, eliminate all but the first active player */
4289
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291     {
4292       if (stored_player[i].active)
4293       {
4294         for (j = i + 1; j < MAX_PLAYERS; j++)
4295         {
4296           if (stored_player[j].active)
4297           {
4298             struct PlayerInfo *player = &stored_player[j];
4299             int jx = player->jx, jy = player->jy;
4300
4301             player->active = FALSE;
4302             player->present = FALSE;
4303
4304             StorePlayer[jx][jy] = 0;
4305             Feld[jx][jy] = EL_EMPTY;
4306           }
4307         }
4308       }
4309     }
4310   }
4311
4312   /* when recording the game, store which players take part in the game */
4313   if (tape.recording)
4314   {
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316     for (i = 0; i < MAX_PLAYERS; i++)
4317       if (stored_player[i].connected)
4318         tape.player_participates[i] = TRUE;
4319 #else
4320     for (i = 0; i < MAX_PLAYERS; i++)
4321       if (stored_player[i].active)
4322         tape.player_participates[i] = TRUE;
4323 #endif
4324   }
4325
4326   if (options.debug)
4327   {
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329     {
4330       struct PlayerInfo *player = &stored_player[i];
4331
4332       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4333              i+1,
4334              player->present,
4335              player->connected,
4336              player->active);
4337       if (local_player == player)
4338         printf("Player  %d is local player.\n", i+1);
4339     }
4340   }
4341
4342   if (BorderElement == EL_EMPTY)
4343   {
4344     SBX_Left = 0;
4345     SBX_Right = lev_fieldx - SCR_FIELDX;
4346     SBY_Upper = 0;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY;
4348   }
4349   else
4350   {
4351     SBX_Left = -1;
4352     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4353     SBY_Upper = -1;
4354     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4355   }
4356
4357 #if NEW_TILESIZE
4358
4359   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4360     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4361
4362   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4363     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4364
4365   if (EVEN(SCR_FIELDX))
4366     SBX_Left--;
4367   if (EVEN(SCR_FIELDY))
4368     SBY_Upper--;
4369
4370 #else
4371
4372   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4373     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4374
4375   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4376     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4377 #endif
4378
4379   /* if local player not found, look for custom element that might create
4380      the player (make some assumptions about the right custom element) */
4381   if (!local_player->present)
4382   {
4383     int start_x = 0, start_y = 0;
4384     int found_rating = 0;
4385     int found_element = EL_UNDEFINED;
4386     int player_nr = local_player->index_nr;
4387
4388     SCAN_PLAYFIELD(x, y)
4389     {
4390       int element = Feld[x][y];
4391       int content;
4392       int xx, yy;
4393       boolean is_player;
4394
4395       if (level.use_start_element[player_nr] &&
4396           level.start_element[player_nr] == element &&
4397           found_rating < 4)
4398       {
4399         start_x = x;
4400         start_y = y;
4401
4402         found_rating = 4;
4403         found_element = element;
4404       }
4405
4406       if (!IS_CUSTOM_ELEMENT(element))
4407         continue;
4408
4409       if (CAN_CHANGE(element))
4410       {
4411         for (i = 0; i < element_info[element].num_change_pages; i++)
4412         {
4413           /* check for player created from custom element as single target */
4414           content = element_info[element].change_page[i].target_element;
4415           is_player = ELEM_IS_PLAYER(content);
4416
4417           if (is_player && (found_rating < 3 ||
4418                             (found_rating == 3 && element < found_element)))
4419           {
4420             start_x = x;
4421             start_y = y;
4422
4423             found_rating = 3;
4424             found_element = element;
4425           }
4426         }
4427       }
4428
4429       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4430       {
4431         /* check for player created from custom element as explosion content */
4432         content = element_info[element].content.e[xx][yy];
4433         is_player = ELEM_IS_PLAYER(content);
4434
4435         if (is_player && (found_rating < 2 ||
4436                           (found_rating == 2 && element < found_element)))
4437         {
4438           start_x = x + xx - 1;
4439           start_y = y + yy - 1;
4440
4441           found_rating = 2;
4442           found_element = element;
4443         }
4444
4445         if (!CAN_CHANGE(element))
4446           continue;
4447
4448         for (i = 0; i < element_info[element].num_change_pages; i++)
4449         {
4450           /* check for player created from custom element as extended target */
4451           content =
4452             element_info[element].change_page[i].target_content.e[xx][yy];
4453
4454           is_player = ELEM_IS_PLAYER(content);
4455
4456           if (is_player && (found_rating < 1 ||
4457                             (found_rating == 1 && element < found_element)))
4458           {
4459             start_x = x + xx - 1;
4460             start_y = y + yy - 1;
4461
4462             found_rating = 1;
4463             found_element = element;
4464           }
4465         }
4466       }
4467     }
4468
4469     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4470                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4471                 start_x - MIDPOSX);
4472
4473     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4474                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4475                 start_y - MIDPOSY);
4476   }
4477   else
4478   {
4479     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4480                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4481                 local_player->jx - MIDPOSX);
4482
4483     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4484                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4485                 local_player->jy - MIDPOSY);
4486   }
4487
4488   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4489
4490 #if 0
4491   /* do not use PLAYING mask for fading out from main screen */
4492   game_status = GAME_MODE_MAIN;
4493 #endif
4494
4495   StopAnimation();
4496
4497   if (!game.restart_level)
4498     CloseDoor(DOOR_CLOSE_1);
4499
4500 #if 1
4501   if (level_editor_test_game)
4502     FadeSkipNextFadeIn();
4503   else
4504     FadeSetEnterScreen();
4505 #else
4506   if (level_editor_test_game)
4507     fading = fading_none;
4508   else
4509     fading = menu.destination;
4510 #endif
4511
4512 #if 1
4513   FadeOut(REDRAW_FIELD);
4514 #else
4515   if (do_fading)
4516     FadeOut(REDRAW_FIELD);
4517 #endif
4518
4519 #if 0
4520   game_status = GAME_MODE_PLAYING;
4521 #endif
4522
4523   /* !!! FIX THIS (START) !!! */
4524   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4525   {
4526     InitGameEngine_EM();
4527
4528     /* blit playfield from scroll buffer to normal back buffer for fading in */
4529     BlitScreenToBitmap_EM(backbuffer);
4530   }
4531   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4532   {
4533     InitGameEngine_SP();
4534
4535     /* blit playfield from scroll buffer to normal back buffer for fading in */
4536     BlitScreenToBitmap_SP(backbuffer);
4537   }
4538   else
4539   {
4540     DrawLevel();
4541     DrawAllPlayers();
4542
4543     /* after drawing the level, correct some elements */
4544     if (game.timegate_time_left == 0)
4545       CloseAllOpenTimegates();
4546
4547 #if NEW_TILESIZE
4548     BlitScreenToBitmap(backbuffer);
4549 #else
4550     /* blit playfield from scroll buffer to normal back buffer for fading in */
4551     if (setup.soft_scrolling)
4552       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4553 #endif
4554
4555     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4556   }
4557   /* !!! FIX THIS (END) !!! */
4558
4559 #if 1
4560   FadeIn(REDRAW_FIELD);
4561 #else
4562   if (do_fading)
4563     FadeIn(REDRAW_FIELD);
4564
4565   BackToFront();
4566 #endif
4567
4568   if (!game.restart_level)
4569   {
4570     /* copy default game door content to main double buffer */
4571 #if 1
4572 #if 1
4573     /* !!! CHECK AGAIN !!! */
4574     SetPanelBackground();
4575     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4576     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4577 #else
4578     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4579
4580     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4581     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4582     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4583                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4584 #endif
4585 #else
4586     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4587                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4588 #endif
4589   }
4590
4591   SetPanelBackground();
4592   SetDrawBackgroundMask(REDRAW_DOOR_1);
4593
4594 #if 1
4595   UpdateAndDisplayGameControlValues();
4596 #else
4597   UpdateGameDoorValues();
4598   DrawGameDoorValues();
4599 #endif
4600
4601   if (!game.restart_level)
4602   {
4603     UnmapGameButtons();
4604     UnmapTapeButtons();
4605     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4606     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4607     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4608     MapGameButtons();
4609     MapTapeButtons();
4610
4611     /* copy actual game door content to door double buffer for OpenDoor() */
4612     BlitBitmap(drawto, bitmap_db_door,
4613                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4614
4615     OpenDoor(DOOR_OPEN_ALL);
4616
4617     PlaySound(SND_GAME_STARTING);
4618
4619     if (setup.sound_music)
4620       PlayLevelMusic();
4621
4622     KeyboardAutoRepeatOffUnlessAutoplay();
4623
4624     if (options.debug)
4625     {
4626       for (i = 0; i < MAX_PLAYERS; i++)
4627         printf("Player %d %sactive.\n",
4628                i + 1, (stored_player[i].active ? "" : "not "));
4629     }
4630   }
4631
4632 #if 1
4633   UnmapAllGadgets();
4634
4635   MapGameButtons();
4636   MapTapeButtons();
4637 #endif
4638
4639   if (!game.restart_level && !tape.playing)
4640   {
4641     LevelStats_incPlayed(level_nr);
4642
4643     SaveLevelSetup_SeriesInfo();
4644
4645 #if 1
4646     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4647 #endif
4648   }
4649
4650   game.restart_level = FALSE;
4651 }
4652
4653 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4654 {
4655   /* this is used for non-R'n'D game engines to update certain engine values */
4656
4657   /* needed to determine if sounds are played within the visible screen area */
4658   scroll_x = actual_scroll_x;
4659   scroll_y = actual_scroll_y;
4660 }
4661
4662 void InitMovDir(int x, int y)
4663 {
4664   int i, element = Feld[x][y];
4665   static int xy[4][2] =
4666   {
4667     {  0, +1 },
4668     { +1,  0 },
4669     {  0, -1 },
4670     { -1,  0 }
4671   };
4672   static int direction[3][4] =
4673   {
4674     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4675     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4676     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4677   };
4678
4679   switch (element)
4680   {
4681     case EL_BUG_RIGHT:
4682     case EL_BUG_UP:
4683     case EL_BUG_LEFT:
4684     case EL_BUG_DOWN:
4685       Feld[x][y] = EL_BUG;
4686       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4687       break;
4688
4689     case EL_SPACESHIP_RIGHT:
4690     case EL_SPACESHIP_UP:
4691     case EL_SPACESHIP_LEFT:
4692     case EL_SPACESHIP_DOWN:
4693       Feld[x][y] = EL_SPACESHIP;
4694       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4695       break;
4696
4697     case EL_BD_BUTTERFLY_RIGHT:
4698     case EL_BD_BUTTERFLY_UP:
4699     case EL_BD_BUTTERFLY_LEFT:
4700     case EL_BD_BUTTERFLY_DOWN:
4701       Feld[x][y] = EL_BD_BUTTERFLY;
4702       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4703       break;
4704
4705     case EL_BD_FIREFLY_RIGHT:
4706     case EL_BD_FIREFLY_UP:
4707     case EL_BD_FIREFLY_LEFT:
4708     case EL_BD_FIREFLY_DOWN:
4709       Feld[x][y] = EL_BD_FIREFLY;
4710       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4711       break;
4712
4713     case EL_PACMAN_RIGHT:
4714     case EL_PACMAN_UP:
4715     case EL_PACMAN_LEFT:
4716     case EL_PACMAN_DOWN:
4717       Feld[x][y] = EL_PACMAN;
4718       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4719       break;
4720
4721     case EL_YAMYAM_LEFT:
4722     case EL_YAMYAM_RIGHT:
4723     case EL_YAMYAM_UP:
4724     case EL_YAMYAM_DOWN:
4725       Feld[x][y] = EL_YAMYAM;
4726       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4727       break;
4728
4729     case EL_SP_SNIKSNAK:
4730       MovDir[x][y] = MV_UP;
4731       break;
4732
4733     case EL_SP_ELECTRON:
4734       MovDir[x][y] = MV_LEFT;
4735       break;
4736
4737     case EL_MOLE_LEFT:
4738     case EL_MOLE_RIGHT:
4739     case EL_MOLE_UP:
4740     case EL_MOLE_DOWN:
4741       Feld[x][y] = EL_MOLE;
4742       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4743       break;
4744
4745     default:
4746       if (IS_CUSTOM_ELEMENT(element))
4747       {
4748         struct ElementInfo *ei = &element_info[element];
4749         int move_direction_initial = ei->move_direction_initial;
4750         int move_pattern = ei->move_pattern;
4751
4752         if (move_direction_initial == MV_START_PREVIOUS)
4753         {
4754           if (MovDir[x][y] != MV_NONE)
4755             return;
4756
4757           move_direction_initial = MV_START_AUTOMATIC;
4758         }
4759
4760         if (move_direction_initial == MV_START_RANDOM)
4761           MovDir[x][y] = 1 << RND(4);
4762         else if (move_direction_initial & MV_ANY_DIRECTION)
4763           MovDir[x][y] = move_direction_initial;
4764         else if (move_pattern == MV_ALL_DIRECTIONS ||
4765                  move_pattern == MV_TURNING_LEFT ||
4766                  move_pattern == MV_TURNING_RIGHT ||
4767                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4768                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4769                  move_pattern == MV_TURNING_RANDOM)
4770           MovDir[x][y] = 1 << RND(4);
4771         else if (move_pattern == MV_HORIZONTAL)
4772           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4773         else if (move_pattern == MV_VERTICAL)
4774           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4775         else if (move_pattern & MV_ANY_DIRECTION)
4776           MovDir[x][y] = element_info[element].move_pattern;
4777         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4778                  move_pattern == MV_ALONG_RIGHT_SIDE)
4779         {
4780           /* use random direction as default start direction */
4781           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4782             MovDir[x][y] = 1 << RND(4);
4783
4784           for (i = 0; i < NUM_DIRECTIONS; i++)
4785           {
4786             int x1 = x + xy[i][0];
4787             int y1 = y + xy[i][1];
4788
4789             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4790             {
4791               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4792                 MovDir[x][y] = direction[0][i];
4793               else
4794                 MovDir[x][y] = direction[1][i];
4795
4796               break;
4797             }
4798           }
4799         }                
4800       }
4801       else
4802       {
4803         MovDir[x][y] = 1 << RND(4);
4804
4805         if (element != EL_BUG &&
4806             element != EL_SPACESHIP &&
4807             element != EL_BD_BUTTERFLY &&
4808             element != EL_BD_FIREFLY)
4809           break;
4810
4811         for (i = 0; i < NUM_DIRECTIONS; i++)
4812         {
4813           int x1 = x + xy[i][0];
4814           int y1 = y + xy[i][1];
4815
4816           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4817           {
4818             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4819             {
4820               MovDir[x][y] = direction[0][i];
4821               break;
4822             }
4823             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4824                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4825             {
4826               MovDir[x][y] = direction[1][i];
4827               break;
4828             }
4829           }
4830         }
4831       }
4832       break;
4833   }
4834
4835   GfxDir[x][y] = MovDir[x][y];
4836 }
4837
4838 void InitAmoebaNr(int x, int y)
4839 {
4840   int i;
4841   int group_nr = AmoebeNachbarNr(x, y);
4842
4843   if (group_nr == 0)
4844   {
4845     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4846     {
4847       if (AmoebaCnt[i] == 0)
4848       {
4849         group_nr = i;
4850         break;
4851       }
4852     }
4853   }
4854
4855   AmoebaNr[x][y] = group_nr;
4856   AmoebaCnt[group_nr]++;
4857   AmoebaCnt2[group_nr]++;
4858 }
4859
4860 static void PlayerWins(struct PlayerInfo *player)
4861 {
4862   player->LevelSolved = TRUE;
4863   player->GameOver = TRUE;
4864
4865   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4866                          level.native_em_level->lev->score : player->score);
4867
4868   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4869   player->LevelSolved_CountingScore = player->score_final;
4870 }
4871
4872 void GameWon()
4873 {
4874   static int time, time_final;
4875   static int score, score_final;
4876   static int game_over_delay_1 = 0;
4877   static int game_over_delay_2 = 0;
4878   int game_over_delay_value_1 = 50;
4879   int game_over_delay_value_2 = 50;
4880
4881   if (!local_player->LevelSolved_GameWon)
4882   {
4883     int i;
4884
4885     /* do not start end game actions before the player stops moving (to exit) */
4886     if (local_player->MovPos)
4887       return;
4888
4889     local_player->LevelSolved_GameWon = TRUE;
4890     local_player->LevelSolved_SaveTape = tape.recording;
4891     local_player->LevelSolved_SaveScore = !tape.playing;
4892
4893     if (!tape.playing)
4894     {
4895       LevelStats_incSolved(level_nr);
4896
4897       SaveLevelSetup_SeriesInfo();
4898
4899 #if 1
4900       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4901 #endif
4902     }
4903
4904     if (tape.auto_play)         /* tape might already be stopped here */
4905       tape.auto_play_level_solved = TRUE;
4906
4907 #if 1
4908     TapeStop();
4909 #endif
4910
4911     game_over_delay_1 = game_over_delay_value_1;
4912     game_over_delay_2 = game_over_delay_value_2;
4913
4914     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4915     score = score_final = local_player->score_final;
4916
4917     if (TimeLeft > 0)
4918     {
4919       time_final = 0;
4920       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4921     }
4922     else if (level.time == 0 && TimePlayed < 999)
4923     {
4924       time_final = 999;
4925       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4926     }
4927
4928     local_player->score_final = score_final;
4929
4930     if (level_editor_test_game)
4931     {
4932       time = time_final;
4933       score = score_final;
4934
4935 #if 1
4936       local_player->LevelSolved_CountingTime = time;
4937       local_player->LevelSolved_CountingScore = score;
4938
4939       game_panel_controls[GAME_PANEL_TIME].value = time;
4940       game_panel_controls[GAME_PANEL_SCORE].value = score;
4941
4942       DisplayGameControlValues();
4943 #else
4944       DrawGameValue_Time(time);
4945       DrawGameValue_Score(score);
4946 #endif
4947     }
4948
4949     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4950     {
4951       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4952       {
4953         /* close exit door after last player */
4954         if ((AllPlayersGone &&
4955              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4956               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4957               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4958             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4959             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4960         {
4961           int element = Feld[ExitX][ExitY];
4962
4963 #if 0
4964           if (element == EL_EM_EXIT_OPEN ||
4965               element == EL_EM_STEEL_EXIT_OPEN)
4966           {
4967             Bang(ExitX, ExitY);
4968           }
4969           else
4970 #endif
4971           {
4972             Feld[ExitX][ExitY] =
4973               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4974                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4975                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4976                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4977                EL_EM_STEEL_EXIT_CLOSING);
4978
4979             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4980           }
4981         }
4982
4983         /* player disappears */
4984         DrawLevelField(ExitX, ExitY);
4985       }
4986
4987       for (i = 0; i < MAX_PLAYERS; i++)
4988       {
4989         struct PlayerInfo *player = &stored_player[i];
4990
4991         if (player->present)
4992         {
4993           RemovePlayer(player);
4994
4995           /* player disappears */
4996           DrawLevelField(player->jx, player->jy);
4997         }
4998       }
4999     }
5000
5001     PlaySound(SND_GAME_WINNING);
5002   }
5003
5004   if (game_over_delay_1 > 0)
5005   {
5006     game_over_delay_1--;
5007
5008     return;
5009   }
5010
5011   if (time != time_final)
5012   {
5013     int time_to_go = ABS(time_final - time);
5014     int time_count_dir = (time < time_final ? +1 : -1);
5015     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5016
5017     time  += time_count_steps * time_count_dir;
5018     score += time_count_steps * level.score[SC_TIME_BONUS];
5019
5020 #if 1
5021     local_player->LevelSolved_CountingTime = time;
5022     local_player->LevelSolved_CountingScore = score;
5023
5024     game_panel_controls[GAME_PANEL_TIME].value = time;
5025     game_panel_controls[GAME_PANEL_SCORE].value = score;
5026
5027     DisplayGameControlValues();
5028 #else
5029     DrawGameValue_Time(time);
5030     DrawGameValue_Score(score);
5031 #endif
5032
5033     if (time == time_final)
5034       StopSound(SND_GAME_LEVELTIME_BONUS);
5035     else if (setup.sound_loops)
5036       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5037     else
5038       PlaySound(SND_GAME_LEVELTIME_BONUS);
5039
5040     return;
5041   }
5042
5043   local_player->LevelSolved_PanelOff = TRUE;
5044
5045   if (game_over_delay_2 > 0)
5046   {
5047     game_over_delay_2--;
5048
5049     return;
5050   }
5051
5052 #if 1
5053   GameEnd();
5054 #endif
5055 }
5056
5057 void GameEnd()
5058 {
5059   int hi_pos;
5060   boolean raise_level = FALSE;
5061
5062   local_player->LevelSolved_GameEnd = TRUE;
5063
5064   CloseDoor(DOOR_CLOSE_1);
5065
5066   if (local_player->LevelSolved_SaveTape)
5067   {
5068 #if 0
5069     TapeStop();
5070 #endif
5071
5072 #if 1
5073     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5074 #else
5075     SaveTape(tape.level_nr);            /* ask to save tape */
5076 #endif
5077   }
5078
5079   if (level_editor_test_game)
5080   {
5081     game_status = GAME_MODE_MAIN;
5082
5083 #if 1
5084     DrawAndFadeInMainMenu(REDRAW_FIELD);
5085 #else
5086     DrawMainMenu();
5087 #endif
5088
5089     return;
5090   }
5091
5092   if (!local_player->LevelSolved_SaveScore)
5093   {
5094 #if 1
5095     FadeOut(REDRAW_FIELD);
5096 #endif
5097
5098     game_status = GAME_MODE_MAIN;
5099
5100     DrawAndFadeInMainMenu(REDRAW_FIELD);
5101
5102     return;
5103   }
5104
5105   if (level_nr == leveldir_current->handicap_level)
5106   {
5107     leveldir_current->handicap_level++;
5108
5109     SaveLevelSetup_SeriesInfo();
5110   }
5111
5112   if (level_nr < leveldir_current->last_level)
5113     raise_level = TRUE;                 /* advance to next level */
5114
5115   if ((hi_pos = NewHiScore()) >= 0) 
5116   {
5117     game_status = GAME_MODE_SCORES;
5118
5119     DrawHallOfFame(hi_pos);
5120
5121     if (raise_level)
5122     {
5123       level_nr++;
5124       TapeErase();
5125     }
5126   }
5127   else
5128   {
5129 #if 1
5130     FadeOut(REDRAW_FIELD);
5131 #endif
5132
5133     game_status = GAME_MODE_MAIN;
5134
5135     if (raise_level)
5136     {
5137       level_nr++;
5138       TapeErase();
5139     }
5140
5141     DrawAndFadeInMainMenu(REDRAW_FIELD);
5142   }
5143 }
5144
5145 int NewHiScore()
5146 {
5147   int k, l;
5148   int position = -1;
5149
5150   LoadScore(level_nr);
5151
5152   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5153       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5154     return -1;
5155
5156   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5157   {
5158     if (local_player->score_final > highscore[k].Score)
5159     {
5160       /* player has made it to the hall of fame */
5161
5162       if (k < MAX_SCORE_ENTRIES - 1)
5163       {
5164         int m = MAX_SCORE_ENTRIES - 1;
5165
5166 #ifdef ONE_PER_NAME
5167         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5168           if (strEqual(setup.player_name, highscore[l].Name))
5169             m = l;
5170         if (m == k)     /* player's new highscore overwrites his old one */
5171           goto put_into_list;
5172 #endif
5173
5174         for (l = m; l > k; l--)
5175         {
5176           strcpy(highscore[l].Name, highscore[l - 1].Name);
5177           highscore[l].Score = highscore[l - 1].Score;
5178         }
5179       }
5180
5181 #ifdef ONE_PER_NAME
5182       put_into_list:
5183 #endif
5184       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5185       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5186       highscore[k].Score = local_player->score_final; 
5187       position = k;
5188       break;
5189     }
5190
5191 #ifdef ONE_PER_NAME
5192     else if (!strncmp(setup.player_name, highscore[k].Name,
5193                       MAX_PLAYER_NAME_LEN))
5194       break;    /* player already there with a higher score */
5195 #endif
5196
5197   }
5198
5199   if (position >= 0) 
5200     SaveScore(level_nr);
5201
5202   return position;
5203 }
5204
5205 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5206 {
5207   int element = Feld[x][y];
5208   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5209   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5210   int horiz_move = (dx != 0);
5211   int sign = (horiz_move ? dx : dy);
5212   int step = sign * element_info[element].move_stepsize;
5213
5214   /* special values for move stepsize for spring and things on conveyor belt */
5215   if (horiz_move)
5216   {
5217     if (CAN_FALL(element) &&
5218         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5219       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5220     else if (element == EL_SPRING)
5221       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5222   }
5223
5224   return step;
5225 }
5226
5227 inline static int getElementMoveStepsize(int x, int y)
5228 {
5229   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5230 }
5231
5232 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5233 {
5234   if (player->GfxAction != action || player->GfxDir != dir)
5235   {
5236 #if 0
5237     printf("Player frame reset! (%d => %d, %d => %d)\n",
5238            player->GfxAction, action, player->GfxDir, dir);
5239 #endif
5240
5241     player->GfxAction = action;
5242     player->GfxDir = dir;
5243     player->Frame = 0;
5244     player->StepFrame = 0;
5245   }
5246 }
5247
5248 #if USE_GFX_RESET_GFX_ANIMATION
5249 static void ResetGfxFrame(int x, int y, boolean redraw)
5250 {
5251   int element = Feld[x][y];
5252   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5253   int last_gfx_frame = GfxFrame[x][y];
5254
5255   if (graphic_info[graphic].anim_global_sync)
5256     GfxFrame[x][y] = FrameCounter;
5257   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5258     GfxFrame[x][y] = CustomValue[x][y];
5259   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5260     GfxFrame[x][y] = element_info[element].collect_score;
5261   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5262     GfxFrame[x][y] = ChangeDelay[x][y];
5263
5264   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5265     DrawLevelGraphicAnimation(x, y, graphic);
5266 }
5267 #endif
5268
5269 static void ResetGfxAnimation(int x, int y)
5270 {
5271   GfxAction[x][y] = ACTION_DEFAULT;
5272   GfxDir[x][y] = MovDir[x][y];
5273   GfxFrame[x][y] = 0;
5274
5275 #if USE_GFX_RESET_GFX_ANIMATION
5276   ResetGfxFrame(x, y, FALSE);
5277 #endif
5278 }
5279
5280 static void ResetRandomAnimationValue(int x, int y)
5281 {
5282   GfxRandom[x][y] = INIT_GFX_RANDOM();
5283 }
5284
5285 void InitMovingField(int x, int y, int direction)
5286 {
5287   int element = Feld[x][y];
5288   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5289   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5290   int newx = x + dx;
5291   int newy = y + dy;
5292   boolean is_moving_before, is_moving_after;
5293 #if 0
5294   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5295 #endif
5296
5297   /* check if element was/is moving or being moved before/after mode change */
5298 #if 1
5299 #if 1
5300   is_moving_before = (WasJustMoving[x][y] != 0);
5301 #else
5302   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5303   is_moving_before = WasJustMoving[x][y];
5304 #endif
5305 #else
5306   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5307 #endif
5308   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5309
5310   /* reset animation only for moving elements which change direction of moving
5311      or which just started or stopped moving
5312      (else CEs with property "can move" / "not moving" are reset each frame) */
5313 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5314 #if 1
5315   if (is_moving_before != is_moving_after ||
5316       direction != MovDir[x][y])
5317     ResetGfxAnimation(x, y);
5318 #else
5319   if ((is_moving_before || is_moving_after) && !continues_moving)
5320     ResetGfxAnimation(x, y);
5321 #endif
5322 #else
5323   if (!continues_moving)
5324     ResetGfxAnimation(x, y);
5325 #endif
5326
5327   MovDir[x][y] = direction;
5328   GfxDir[x][y] = direction;
5329
5330 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5331   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5332                      direction == MV_DOWN && CAN_FALL(element) ?
5333                      ACTION_FALLING : ACTION_MOVING);
5334 #else
5335   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5336                      ACTION_FALLING : ACTION_MOVING);
5337 #endif
5338
5339   /* this is needed for CEs with property "can move" / "not moving" */
5340
5341   if (is_moving_after)
5342   {
5343     if (Feld[newx][newy] == EL_EMPTY)
5344       Feld[newx][newy] = EL_BLOCKED;
5345
5346     MovDir[newx][newy] = MovDir[x][y];
5347
5348 #if USE_NEW_CUSTOM_VALUE
5349     CustomValue[newx][newy] = CustomValue[x][y];
5350 #endif
5351
5352     GfxFrame[newx][newy] = GfxFrame[x][y];
5353     GfxRandom[newx][newy] = GfxRandom[x][y];
5354     GfxAction[newx][newy] = GfxAction[x][y];
5355     GfxDir[newx][newy] = GfxDir[x][y];
5356   }
5357 }
5358
5359 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5360 {
5361   int direction = MovDir[x][y];
5362   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5363   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5364
5365   *goes_to_x = newx;
5366   *goes_to_y = newy;
5367 }
5368
5369 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5370 {
5371   int oldx = x, oldy = y;
5372   int direction = MovDir[x][y];
5373
5374   if (direction == MV_LEFT)
5375     oldx++;
5376   else if (direction == MV_RIGHT)
5377     oldx--;
5378   else if (direction == MV_UP)
5379     oldy++;
5380   else if (direction == MV_DOWN)
5381     oldy--;
5382
5383   *comes_from_x = oldx;
5384   *comes_from_y = oldy;
5385 }
5386
5387 int MovingOrBlocked2Element(int x, int y)
5388 {
5389   int element = Feld[x][y];
5390
5391   if (element == EL_BLOCKED)
5392   {
5393     int oldx, oldy;
5394
5395     Blocked2Moving(x, y, &oldx, &oldy);
5396     return Feld[oldx][oldy];
5397   }
5398   else
5399     return element;
5400 }
5401
5402 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5403 {
5404   /* like MovingOrBlocked2Element(), but if element is moving
5405      and (x,y) is the field the moving element is just leaving,
5406      return EL_BLOCKED instead of the element value */
5407   int element = Feld[x][y];
5408
5409   if (IS_MOVING(x, y))
5410   {
5411     if (element == EL_BLOCKED)
5412     {
5413       int oldx, oldy;
5414
5415       Blocked2Moving(x, y, &oldx, &oldy);
5416       return Feld[oldx][oldy];
5417     }
5418     else
5419       return EL_BLOCKED;
5420   }
5421   else
5422     return element;
5423 }
5424
5425 static void RemoveField(int x, int y)
5426 {
5427   Feld[x][y] = EL_EMPTY;
5428
5429   MovPos[x][y] = 0;
5430   MovDir[x][y] = 0;
5431   MovDelay[x][y] = 0;
5432
5433 #if USE_NEW_CUSTOM_VALUE
5434   CustomValue[x][y] = 0;
5435 #endif
5436
5437   AmoebaNr[x][y] = 0;
5438   ChangeDelay[x][y] = 0;
5439   ChangePage[x][y] = -1;
5440   Pushed[x][y] = FALSE;
5441
5442 #if 0
5443   ExplodeField[x][y] = EX_TYPE_NONE;
5444 #endif
5445
5446   GfxElement[x][y] = EL_UNDEFINED;
5447   GfxAction[x][y] = ACTION_DEFAULT;
5448   GfxDir[x][y] = MV_NONE;
5449 #if 0
5450   /* !!! this would prevent the removed tile from being redrawn !!! */
5451   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5452 #endif
5453 }
5454
5455 void RemoveMovingField(int x, int y)
5456 {
5457   int oldx = x, oldy = y, newx = x, newy = y;
5458   int element = Feld[x][y];
5459   int next_element = EL_UNDEFINED;
5460
5461   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5462     return;
5463
5464   if (IS_MOVING(x, y))
5465   {
5466     Moving2Blocked(x, y, &newx, &newy);
5467
5468     if (Feld[newx][newy] != EL_BLOCKED)
5469     {
5470       /* element is moving, but target field is not free (blocked), but
5471          already occupied by something different (example: acid pool);
5472          in this case, only remove the moving field, but not the target */
5473
5474       RemoveField(oldx, oldy);
5475
5476       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5477
5478       TEST_DrawLevelField(oldx, oldy);
5479
5480       return;
5481     }
5482   }
5483   else if (element == EL_BLOCKED)
5484   {
5485     Blocked2Moving(x, y, &oldx, &oldy);
5486     if (!IS_MOVING(oldx, oldy))
5487       return;
5488   }
5489
5490   if (element == EL_BLOCKED &&
5491       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5492        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5493        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5494        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5495        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5496        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5497     next_element = get_next_element(Feld[oldx][oldy]);
5498
5499   RemoveField(oldx, oldy);
5500   RemoveField(newx, newy);
5501
5502   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5503
5504   if (next_element != EL_UNDEFINED)
5505     Feld[oldx][oldy] = next_element;
5506
5507   TEST_DrawLevelField(oldx, oldy);
5508   TEST_DrawLevelField(newx, newy);
5509 }
5510
5511 void DrawDynamite(int x, int y)
5512 {
5513   int sx = SCREENX(x), sy = SCREENY(y);
5514   int graphic = el2img(Feld[x][y]);
5515   int frame;
5516
5517   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5518     return;
5519
5520   if (IS_WALKABLE_INSIDE(Back[x][y]))
5521     return;
5522
5523   if (Back[x][y])
5524     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5525   else if (Store[x][y])
5526     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5527
5528   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5529
5530   if (Back[x][y] || Store[x][y])
5531     DrawGraphicThruMask(sx, sy, graphic, frame);
5532   else
5533     DrawGraphic(sx, sy, graphic, frame);
5534 }
5535
5536 void CheckDynamite(int x, int y)
5537 {
5538   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5539   {
5540     MovDelay[x][y]--;
5541
5542     if (MovDelay[x][y] != 0)
5543     {
5544       DrawDynamite(x, y);
5545       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5546
5547       return;
5548     }
5549   }
5550
5551   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5552
5553   Bang(x, y);
5554 }
5555
5556 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5557 {
5558   boolean num_checked_players = 0;
5559   int i;
5560
5561   for (i = 0; i < MAX_PLAYERS; i++)
5562   {
5563     if (stored_player[i].active)
5564     {
5565       int sx = stored_player[i].jx;
5566       int sy = stored_player[i].jy;
5567
5568       if (num_checked_players == 0)
5569       {
5570         *sx1 = *sx2 = sx;
5571         *sy1 = *sy2 = sy;
5572       }
5573       else
5574       {
5575         *sx1 = MIN(*sx1, sx);
5576         *sy1 = MIN(*sy1, sy);
5577         *sx2 = MAX(*sx2, sx);
5578         *sy2 = MAX(*sy2, sy);
5579       }
5580
5581       num_checked_players++;
5582     }
5583   }
5584 }
5585
5586 static boolean checkIfAllPlayersFitToScreen_RND()
5587 {
5588   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5589
5590   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5591
5592   return (sx2 - sx1 < SCR_FIELDX &&
5593           sy2 - sy1 < SCR_FIELDY);
5594 }
5595
5596 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5597 {
5598   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5599
5600   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5601
5602   *sx = (sx1 + sx2) / 2;
5603   *sy = (sy1 + sy2) / 2;
5604 }
5605
5606 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5607                         boolean center_screen, boolean quick_relocation)
5608 {
5609   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5610   boolean no_delay = (tape.warp_forward);
5611   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5612   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5613
5614   if (quick_relocation)
5615   {
5616     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5617     {
5618       if (!level.shifted_relocation || center_screen)
5619       {
5620         /* quick relocation (without scrolling), with centering of screen */
5621
5622         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5623                     x > SBX_Right + MIDPOSX ? SBX_Right :
5624                     x - MIDPOSX);
5625
5626         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5627                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5628                     y - MIDPOSY);
5629       }
5630       else
5631       {
5632         /* quick relocation (without scrolling), but do not center screen */
5633
5634         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5635                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5636                                old_x - MIDPOSX);
5637
5638         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5639                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5640                                old_y - MIDPOSY);
5641
5642         int offset_x = x + (scroll_x - center_scroll_x);
5643         int offset_y = y + (scroll_y - center_scroll_y);
5644
5645         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5646                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5647                     offset_x - MIDPOSX);
5648
5649         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5650                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651                     offset_y - MIDPOSY);
5652       }
5653     }
5654     else
5655     {
5656 #if 1
5657       if (!level.shifted_relocation || center_screen)
5658       {
5659         /* quick relocation (without scrolling), with centering of screen */
5660
5661         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5662                     x > SBX_Right + MIDPOSX ? SBX_Right :
5663                     x - MIDPOSX);
5664
5665         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5666                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5667                     y - MIDPOSY);
5668       }
5669       else
5670       {
5671         /* quick relocation (without scrolling), but do not center screen */
5672
5673         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5674                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5675                                old_x - MIDPOSX);
5676
5677         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5678                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5679                                old_y - MIDPOSY);
5680
5681         int offset_x = x + (scroll_x - center_scroll_x);
5682         int offset_y = y + (scroll_y - center_scroll_y);
5683
5684         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5685                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5686                     offset_x - MIDPOSX);
5687
5688         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5689                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5690                     offset_y - MIDPOSY);
5691       }
5692 #else
5693       /* quick relocation (without scrolling), inside visible screen area */
5694
5695       int offset = game.scroll_delay_value;
5696
5697       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5698           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5699         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5700
5701       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5702           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5703         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5704
5705       /* don't scroll over playfield boundaries */
5706       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5707         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5708
5709       /* don't scroll over playfield boundaries */
5710       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5711         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5712 #endif
5713     }
5714
5715     RedrawPlayfield(TRUE, 0,0,0,0);
5716   }
5717   else
5718   {
5719 #if 1
5720     int scroll_xx, scroll_yy;
5721
5722     if (!level.shifted_relocation || center_screen)
5723     {
5724       /* visible relocation (with scrolling), with centering of screen */
5725
5726       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5727                    x > SBX_Right + MIDPOSX ? SBX_Right :
5728                    x - MIDPOSX);
5729
5730       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5731                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5732                    y - MIDPOSY);
5733     }
5734     else
5735     {
5736       /* visible relocation (with scrolling), but do not center screen */
5737
5738       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5739                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5740                              old_x - MIDPOSX);
5741
5742       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5743                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5744                              old_y - MIDPOSY);
5745
5746       int offset_x = x + (scroll_x - center_scroll_x);
5747       int offset_y = y + (scroll_y - center_scroll_y);
5748
5749       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5750                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5751                    offset_x - MIDPOSX);
5752
5753       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5754                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5755                    offset_y - MIDPOSY);
5756     }
5757
5758 #else
5759
5760     /* visible relocation (with scrolling), with centering of screen */
5761
5762     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5763                      x > SBX_Right + MIDPOSX ? SBX_Right :
5764                      x - MIDPOSX);
5765
5766     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5767                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5768                      y - MIDPOSY);
5769 #endif
5770
5771     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5772
5773     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5774     {
5775       int dx = 0, dy = 0;
5776       int fx = FX, fy = FY;
5777
5778       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5779       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5780
5781       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5782         break;
5783
5784       scroll_x -= dx;
5785       scroll_y -= dy;
5786
5787       fx += dx * TILEX / 2;
5788       fy += dy * TILEY / 2;
5789
5790       ScrollLevel(dx, dy);
5791       DrawAllPlayers();
5792
5793       /* scroll in two steps of half tile size to make things smoother */
5794       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5795       FlushDisplay();
5796       Delay(wait_delay_value);
5797
5798       /* scroll second step to align at full tile size */
5799       BackToFront();
5800       Delay(wait_delay_value);
5801     }
5802
5803     DrawAllPlayers();
5804     BackToFront();
5805     Delay(wait_delay_value);
5806   }
5807 }
5808
5809 void RelocatePlayer(int jx, int jy, int el_player_raw)
5810 {
5811   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5812   int player_nr = GET_PLAYER_NR(el_player);
5813   struct PlayerInfo *player = &stored_player[player_nr];
5814   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5815   boolean no_delay = (tape.warp_forward);
5816   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5817   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5818   int old_jx = player->jx;
5819   int old_jy = player->jy;
5820   int old_element = Feld[old_jx][old_jy];
5821   int element = Feld[jx][jy];
5822   boolean player_relocated = (old_jx != jx || old_jy != jy);
5823
5824   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5825   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5826   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5827   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5828   int leave_side_horiz = move_dir_horiz;
5829   int leave_side_vert  = move_dir_vert;
5830   int enter_side = enter_side_horiz | enter_side_vert;
5831   int leave_side = leave_side_horiz | leave_side_vert;
5832
5833   if (player->GameOver)         /* do not reanimate dead player */
5834     return;
5835
5836   if (!player_relocated)        /* no need to relocate the player */
5837     return;
5838
5839   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5840   {
5841     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5842     DrawLevelField(jx, jy);
5843   }
5844
5845   if (player->present)
5846   {
5847     while (player->MovPos)
5848     {
5849       ScrollPlayer(player, SCROLL_GO_ON);
5850       ScrollScreen(NULL, SCROLL_GO_ON);
5851
5852       AdvanceFrameAndPlayerCounters(player->index_nr);
5853
5854       DrawPlayer(player);
5855
5856       BackToFront();
5857       Delay(wait_delay_value);
5858     }
5859
5860     DrawPlayer(player);         /* needed here only to cleanup last field */
5861     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5862
5863     player->is_moving = FALSE;
5864   }
5865
5866   if (IS_CUSTOM_ELEMENT(old_element))
5867     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5868                                CE_LEFT_BY_PLAYER,
5869                                player->index_bit, leave_side);
5870
5871   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5872                                       CE_PLAYER_LEAVES_X,
5873                                       player->index_bit, leave_side);
5874
5875   Feld[jx][jy] = el_player;
5876   InitPlayerField(jx, jy, el_player, TRUE);
5877
5878   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5879      possible that the relocation target field did not contain a player element,
5880      but a walkable element, to which the new player was relocated -- in this
5881      case, restore that (already initialized!) element on the player field */
5882   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5883   {
5884     Feld[jx][jy] = element;     /* restore previously existing element */
5885 #if 0
5886     /* !!! do not initialize already initialized element a second time !!! */
5887     /* (this causes at least problems with "element creation" CE trigger for
5888        already existing elements, and existing Sokoban fields counted twice) */
5889     InitField(jx, jy, FALSE);
5890 #endif
5891   }
5892
5893   /* only visually relocate centered player */
5894   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5895                      FALSE, level.instant_relocation);
5896
5897   TestIfPlayerTouchesBadThing(jx, jy);
5898   TestIfPlayerTouchesCustomElement(jx, jy);
5899
5900   if (IS_CUSTOM_ELEMENT(element))
5901     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5902                                player->index_bit, enter_side);
5903
5904   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5905                                       player->index_bit, enter_side);
5906
5907 #if 1
5908   if (player->is_switching)
5909   {
5910     /* ensure that relocation while still switching an element does not cause
5911        a new element to be treated as also switched directly after relocation
5912        (this is important for teleporter switches that teleport the player to
5913        a place where another teleporter switch is in the same direction, which
5914        would then incorrectly be treated as immediately switched before the
5915        direction key that caused the switch was released) */
5916
5917     player->switch_x += jx - old_jx;
5918     player->switch_y += jy - old_jy;
5919   }
5920 #endif
5921 }
5922
5923 void Explode(int ex, int ey, int phase, int mode)
5924 {
5925   int x, y;
5926   int last_phase;
5927   int border_element;
5928
5929   /* !!! eliminate this variable !!! */
5930   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5931
5932   if (game.explosions_delayed)
5933   {
5934     ExplodeField[ex][ey] = mode;
5935     return;
5936   }
5937
5938   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5939   {
5940     int center_element = Feld[ex][ey];
5941     int artwork_element, explosion_element;     /* set these values later */
5942
5943 #if 0
5944     /* --- This is only really needed (and now handled) in "Impact()". --- */
5945     /* do not explode moving elements that left the explode field in time */
5946     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5947         center_element == EL_EMPTY &&
5948         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5949       return;
5950 #endif
5951
5952 #if 0
5953     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5954     if (mode == EX_TYPE_NORMAL ||
5955         mode == EX_TYPE_CENTER ||
5956         mode == EX_TYPE_CROSS)
5957       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5958 #endif
5959
5960     /* remove things displayed in background while burning dynamite */
5961     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5962       Back[ex][ey] = 0;
5963
5964     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5965     {
5966       /* put moving element to center field (and let it explode there) */
5967       center_element = MovingOrBlocked2Element(ex, ey);
5968       RemoveMovingField(ex, ey);
5969       Feld[ex][ey] = center_element;
5970     }
5971
5972     /* now "center_element" is finally determined -- set related values now */
5973     artwork_element = center_element;           /* for custom player artwork */
5974     explosion_element = center_element;         /* for custom player artwork */
5975
5976     if (IS_PLAYER(ex, ey))
5977     {
5978       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5979
5980       artwork_element = stored_player[player_nr].artwork_element;
5981
5982       if (level.use_explosion_element[player_nr])
5983       {
5984         explosion_element = level.explosion_element[player_nr];
5985         artwork_element = explosion_element;
5986       }
5987     }
5988
5989 #if 1
5990     if (mode == EX_TYPE_NORMAL ||
5991         mode == EX_TYPE_CENTER ||
5992         mode == EX_TYPE_CROSS)
5993       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5994 #endif
5995
5996     last_phase = element_info[explosion_element].explosion_delay + 1;
5997
5998     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5999     {
6000       int xx = x - ex + 1;
6001       int yy = y - ey + 1;
6002       int element;
6003
6004       if (!IN_LEV_FIELD(x, y) ||
6005           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6006           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6007         continue;
6008
6009       element = Feld[x][y];
6010
6011       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6012       {
6013         element = MovingOrBlocked2Element(x, y);
6014
6015         if (!IS_EXPLOSION_PROOF(element))
6016           RemoveMovingField(x, y);
6017       }
6018
6019       /* indestructible elements can only explode in center (but not flames) */
6020       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6021                                            mode == EX_TYPE_BORDER)) ||
6022           element == EL_FLAMES)
6023         continue;
6024
6025       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6026          behaviour, for example when touching a yamyam that explodes to rocks
6027          with active deadly shield, a rock is created under the player !!! */
6028       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6029 #if 0
6030       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6031           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6032            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6033 #else
6034       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6035 #endif
6036       {
6037         if (IS_ACTIVE_BOMB(element))
6038         {
6039           /* re-activate things under the bomb like gate or penguin */
6040           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6041           Back[x][y] = 0;
6042         }
6043
6044         continue;
6045       }
6046
6047       /* save walkable background elements while explosion on same tile */
6048       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6049           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6050         Back[x][y] = element;
6051
6052       /* ignite explodable elements reached by other explosion */
6053       if (element == EL_EXPLOSION)
6054         element = Store2[x][y];
6055
6056       if (AmoebaNr[x][y] &&
6057           (element == EL_AMOEBA_FULL ||
6058            element == EL_BD_AMOEBA ||
6059            element == EL_AMOEBA_GROWING))
6060       {
6061         AmoebaCnt[AmoebaNr[x][y]]--;
6062         AmoebaCnt2[AmoebaNr[x][y]]--;
6063       }
6064
6065       RemoveField(x, y);
6066
6067       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6068       {
6069         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6070
6071         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6072
6073         if (PLAYERINFO(ex, ey)->use_murphy)
6074           Store[x][y] = EL_EMPTY;
6075       }
6076
6077       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6078          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6079       else if (ELEM_IS_PLAYER(center_element))
6080         Store[x][y] = EL_EMPTY;
6081       else if (center_element == EL_YAMYAM)
6082         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6083       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6084         Store[x][y] = element_info[center_element].content.e[xx][yy];
6085 #if 1
6086       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6087          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6088          otherwise) -- FIX THIS !!! */
6089       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6090         Store[x][y] = element_info[element].content.e[1][1];
6091 #else
6092       else if (!CAN_EXPLODE(element))
6093         Store[x][y] = element_info[element].content.e[1][1];
6094 #endif
6095       else
6096         Store[x][y] = EL_EMPTY;
6097
6098       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6099           center_element == EL_AMOEBA_TO_DIAMOND)
6100         Store2[x][y] = element;
6101
6102       Feld[x][y] = EL_EXPLOSION;
6103       GfxElement[x][y] = artwork_element;
6104
6105       ExplodePhase[x][y] = 1;
6106       ExplodeDelay[x][y] = last_phase;
6107
6108       Stop[x][y] = TRUE;
6109     }
6110
6111     if (center_element == EL_YAMYAM)
6112       game.yamyam_content_nr =
6113         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6114
6115     return;
6116   }
6117
6118   if (Stop[ex][ey])
6119     return;
6120
6121   x = ex;
6122   y = ey;
6123
6124   if (phase == 1)
6125     GfxFrame[x][y] = 0;         /* restart explosion animation */
6126
6127   last_phase = ExplodeDelay[x][y];
6128
6129   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6130
6131 #ifdef DEBUG
6132
6133   /* activate this even in non-DEBUG version until cause for crash in
6134      getGraphicAnimationFrame() (see below) is found and eliminated */
6135
6136 #endif
6137 #if 1
6138
6139 #if 1
6140   /* this can happen if the player leaves an explosion just in time */
6141   if (GfxElement[x][y] == EL_UNDEFINED)
6142     GfxElement[x][y] = EL_EMPTY;
6143 #else
6144   if (GfxElement[x][y] == EL_UNDEFINED)
6145   {
6146     printf("\n\n");
6147     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6148     printf("Explode(): This should never happen!\n");
6149     printf("\n\n");
6150
6151     GfxElement[x][y] = EL_EMPTY;
6152   }
6153 #endif
6154
6155 #endif
6156
6157   border_element = Store2[x][y];
6158   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6159     border_element = StorePlayer[x][y];
6160
6161   if (phase == element_info[border_element].ignition_delay ||
6162       phase == last_phase)
6163   {
6164     boolean border_explosion = FALSE;
6165
6166     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6167         !PLAYER_EXPLOSION_PROTECTED(x, y))
6168     {
6169       KillPlayerUnlessExplosionProtected(x, y);
6170       border_explosion = TRUE;
6171     }
6172     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6173     {
6174       Feld[x][y] = Store2[x][y];
6175       Store2[x][y] = 0;
6176       Bang(x, y);
6177       border_explosion = TRUE;
6178     }
6179     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6180     {
6181       AmoebeUmwandeln(x, y);
6182       Store2[x][y] = 0;
6183       border_explosion = TRUE;
6184     }
6185
6186     /* if an element just explodes due to another explosion (chain-reaction),
6187        do not immediately end the new explosion when it was the last frame of
6188        the explosion (as it would be done in the following "if"-statement!) */
6189     if (border_explosion && phase == last_phase)
6190       return;
6191   }
6192
6193   if (phase == last_phase)
6194   {
6195     int element;
6196
6197     element = Feld[x][y] = Store[x][y];
6198     Store[x][y] = Store2[x][y] = 0;
6199     GfxElement[x][y] = EL_UNDEFINED;
6200
6201     /* player can escape from explosions and might therefore be still alive */
6202     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6203         element <= EL_PLAYER_IS_EXPLODING_4)
6204     {
6205       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6206       int explosion_element = EL_PLAYER_1 + player_nr;
6207       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6208       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6209
6210       if (level.use_explosion_element[player_nr])
6211         explosion_element = level.explosion_element[player_nr];
6212
6213       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6214                     element_info[explosion_element].content.e[xx][yy]);
6215     }
6216
6217     /* restore probably existing indestructible background element */
6218     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6219       element = Feld[x][y] = Back[x][y];
6220     Back[x][y] = 0;
6221
6222     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6223     GfxDir[x][y] = MV_NONE;
6224     ChangeDelay[x][y] = 0;
6225     ChangePage[x][y] = -1;
6226
6227 #if USE_NEW_CUSTOM_VALUE
6228     CustomValue[x][y] = 0;
6229 #endif
6230
6231     InitField_WithBug2(x, y, FALSE);
6232
6233     TEST_DrawLevelField(x, y);
6234
6235     TestIfElementTouchesCustomElement(x, y);
6236
6237     if (GFX_CRUMBLED(element))
6238       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6239
6240     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6241       StorePlayer[x][y] = 0;
6242
6243     if (ELEM_IS_PLAYER(element))
6244       RelocatePlayer(x, y, element);
6245   }
6246   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6247   {
6248     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6249     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6250
6251     if (phase == delay)
6252       TEST_DrawLevelFieldCrumbled(x, y);
6253
6254     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6255     {
6256       DrawLevelElement(x, y, Back[x][y]);
6257       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6258     }
6259     else if (IS_WALKABLE_UNDER(Back[x][y]))
6260     {
6261       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6262       DrawLevelElementThruMask(x, y, Back[x][y]);
6263     }
6264     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6265       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6266   }
6267 }
6268
6269 void DynaExplode(int ex, int ey)
6270 {
6271   int i, j;
6272   int dynabomb_element = Feld[ex][ey];
6273   int dynabomb_size = 1;
6274   boolean dynabomb_xl = FALSE;
6275   struct PlayerInfo *player;
6276   static int xy[4][2] =
6277   {
6278     { 0, -1 },
6279     { -1, 0 },
6280     { +1, 0 },
6281     { 0, +1 }
6282   };
6283
6284   if (IS_ACTIVE_BOMB(dynabomb_element))
6285   {
6286     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6287     dynabomb_size = player->dynabomb_size;
6288     dynabomb_xl = player->dynabomb_xl;
6289     player->dynabombs_left++;
6290   }
6291
6292   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6293
6294   for (i = 0; i < NUM_DIRECTIONS; i++)
6295   {
6296     for (j = 1; j <= dynabomb_size; j++)
6297     {
6298       int x = ex + j * xy[i][0];
6299       int y = ey + j * xy[i][1];
6300       int element;
6301
6302       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6303         break;
6304
6305       element = Feld[x][y];
6306
6307       /* do not restart explosions of fields with active bombs */
6308       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6309         continue;
6310
6311       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6312
6313       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6314           !IS_DIGGABLE(element) && !dynabomb_xl)
6315         break;
6316     }
6317   }
6318 }
6319
6320 void Bang(int x, int y)
6321 {
6322   int element = MovingOrBlocked2Element(x, y);
6323   int explosion_type = EX_TYPE_NORMAL;
6324
6325   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6326   {
6327     struct PlayerInfo *player = PLAYERINFO(x, y);
6328
6329 #if USE_FIX_CE_ACTION_WITH_PLAYER
6330     element = Feld[x][y] = player->initial_element;
6331 #else
6332     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6333                             player->element_nr);
6334 #endif
6335
6336     if (level.use_explosion_element[player->index_nr])
6337     {
6338       int explosion_element = level.explosion_element[player->index_nr];
6339
6340       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6341         explosion_type = EX_TYPE_CROSS;
6342       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6343         explosion_type = EX_TYPE_CENTER;
6344     }
6345   }
6346
6347   switch (element)
6348   {
6349     case EL_BUG:
6350     case EL_SPACESHIP:
6351     case EL_BD_BUTTERFLY:
6352     case EL_BD_FIREFLY:
6353     case EL_YAMYAM:
6354     case EL_DARK_YAMYAM:
6355     case EL_ROBOT:
6356     case EL_PACMAN:
6357     case EL_MOLE:
6358       RaiseScoreElement(element);
6359       break;
6360
6361     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6362     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6363     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6364     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6365     case EL_DYNABOMB_INCREASE_NUMBER:
6366     case EL_DYNABOMB_INCREASE_SIZE:
6367     case EL_DYNABOMB_INCREASE_POWER:
6368       explosion_type = EX_TYPE_DYNA;
6369       break;
6370
6371     case EL_DC_LANDMINE:
6372 #if 0
6373     case EL_EM_EXIT_OPEN:
6374     case EL_EM_STEEL_EXIT_OPEN:
6375 #endif
6376       explosion_type = EX_TYPE_CENTER;
6377       break;
6378
6379     case EL_PENGUIN:
6380     case EL_LAMP:
6381     case EL_LAMP_ACTIVE:
6382     case EL_AMOEBA_TO_DIAMOND:
6383       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6384         explosion_type = EX_TYPE_CENTER;
6385       break;
6386
6387     default:
6388       if (element_info[element].explosion_type == EXPLODES_CROSS)
6389         explosion_type = EX_TYPE_CROSS;
6390       else if (element_info[element].explosion_type == EXPLODES_1X1)
6391         explosion_type = EX_TYPE_CENTER;
6392       break;
6393   }
6394
6395   if (explosion_type == EX_TYPE_DYNA)
6396     DynaExplode(x, y);
6397   else
6398     Explode(x, y, EX_PHASE_START, explosion_type);
6399
6400   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6401 }
6402
6403 void SplashAcid(int x, int y)
6404 {
6405   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6406       (!IN_LEV_FIELD(x - 1, y - 2) ||
6407        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6408     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6409
6410   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6411       (!IN_LEV_FIELD(x + 1, y - 2) ||
6412        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6413     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6414
6415   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6416 }
6417
6418 static void InitBeltMovement()
6419 {
6420   static int belt_base_element[4] =
6421   {
6422     EL_CONVEYOR_BELT_1_LEFT,
6423     EL_CONVEYOR_BELT_2_LEFT,
6424     EL_CONVEYOR_BELT_3_LEFT,
6425     EL_CONVEYOR_BELT_4_LEFT
6426   };
6427   static int belt_base_active_element[4] =
6428   {
6429     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6430     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6431     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6432     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6433   };
6434
6435   int x, y, i, j;
6436
6437   /* set frame order for belt animation graphic according to belt direction */
6438   for (i = 0; i < NUM_BELTS; i++)
6439   {
6440     int belt_nr = i;
6441
6442     for (j = 0; j < NUM_BELT_PARTS; j++)
6443     {
6444       int element = belt_base_active_element[belt_nr] + j;
6445       int graphic_1 = el2img(element);
6446       int graphic_2 = el2panelimg(element);
6447
6448       if (game.belt_dir[i] == MV_LEFT)
6449       {
6450         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6451         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6452       }
6453       else
6454       {
6455         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6456         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6457       }
6458     }
6459   }
6460
6461   SCAN_PLAYFIELD(x, y)
6462   {
6463     int element = Feld[x][y];
6464
6465     for (i = 0; i < NUM_BELTS; i++)
6466     {
6467       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6468       {
6469         int e_belt_nr = getBeltNrFromBeltElement(element);
6470         int belt_nr = i;
6471
6472         if (e_belt_nr == belt_nr)
6473         {
6474           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6475
6476           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6477         }
6478       }
6479     }
6480   }
6481 }
6482
6483 static void ToggleBeltSwitch(int x, int y)
6484 {
6485   static int belt_base_element[4] =
6486   {
6487     EL_CONVEYOR_BELT_1_LEFT,
6488     EL_CONVEYOR_BELT_2_LEFT,
6489     EL_CONVEYOR_BELT_3_LEFT,
6490     EL_CONVEYOR_BELT_4_LEFT
6491   };
6492   static int belt_base_active_element[4] =
6493   {
6494     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6495     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6496     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6497     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6498   };
6499   static int belt_base_switch_element[4] =
6500   {
6501     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6502     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6503     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6504     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6505   };
6506   static int belt_move_dir[4] =
6507   {
6508     MV_LEFT,
6509     MV_NONE,
6510     MV_RIGHT,
6511     MV_NONE,
6512   };
6513
6514   int element = Feld[x][y];
6515   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6516   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6517   int belt_dir = belt_move_dir[belt_dir_nr];
6518   int xx, yy, i;
6519
6520   if (!IS_BELT_SWITCH(element))
6521     return;
6522
6523   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6524   game.belt_dir[belt_nr] = belt_dir;
6525
6526   if (belt_dir_nr == 3)
6527     belt_dir_nr = 1;
6528
6529   /* set frame order for belt animation graphic according to belt direction */
6530   for (i = 0; i < NUM_BELT_PARTS; i++)
6531   {
6532     int element = belt_base_active_element[belt_nr] + i;
6533     int graphic_1 = el2img(element);
6534     int graphic_2 = el2panelimg(element);
6535
6536     if (belt_dir == MV_LEFT)
6537     {
6538       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6539       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6540     }
6541     else
6542     {
6543       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6544       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6545     }
6546   }
6547
6548   SCAN_PLAYFIELD(xx, yy)
6549   {
6550     int element = Feld[xx][yy];
6551
6552     if (IS_BELT_SWITCH(element))
6553     {
6554       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6555
6556       if (e_belt_nr == belt_nr)
6557       {
6558         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6559         TEST_DrawLevelField(xx, yy);
6560       }
6561     }
6562     else if (IS_BELT(element) && belt_dir != MV_NONE)
6563     {
6564       int e_belt_nr = getBeltNrFromBeltElement(element);
6565
6566       if (e_belt_nr == belt_nr)
6567       {
6568         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6569
6570         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6571         TEST_DrawLevelField(xx, yy);
6572       }
6573     }
6574     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6575     {
6576       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6577
6578       if (e_belt_nr == belt_nr)
6579       {
6580         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6581
6582         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6583         TEST_DrawLevelField(xx, yy);
6584       }
6585     }
6586   }
6587 }
6588
6589 static void ToggleSwitchgateSwitch(int x, int y)
6590 {
6591   int xx, yy;
6592
6593   game.switchgate_pos = !game.switchgate_pos;
6594
6595   SCAN_PLAYFIELD(xx, yy)
6596   {
6597     int element = Feld[xx][yy];
6598
6599 #if !USE_BOTH_SWITCHGATE_SWITCHES
6600     if (element == EL_SWITCHGATE_SWITCH_UP ||
6601         element == EL_SWITCHGATE_SWITCH_DOWN)
6602     {
6603       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6604       TEST_DrawLevelField(xx, yy);
6605     }
6606     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6607              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6608     {
6609       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6610       TEST_DrawLevelField(xx, yy);
6611     }
6612 #else
6613     if (element == EL_SWITCHGATE_SWITCH_UP)
6614     {
6615       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6616       TEST_DrawLevelField(xx, yy);
6617     }
6618     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6619     {
6620       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6621       TEST_DrawLevelField(xx, yy);
6622     }
6623     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6624     {
6625       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6626       TEST_DrawLevelField(xx, yy);
6627     }
6628     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6629     {
6630       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633 #endif
6634     else if (element == EL_SWITCHGATE_OPEN ||
6635              element == EL_SWITCHGATE_OPENING)
6636     {
6637       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6638
6639       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6640     }
6641     else if (element == EL_SWITCHGATE_CLOSED ||
6642              element == EL_SWITCHGATE_CLOSING)
6643     {
6644       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6645
6646       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6647     }
6648   }
6649 }
6650
6651 static int getInvisibleActiveFromInvisibleElement(int element)
6652 {
6653   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6654           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6655           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6656           element);
6657 }
6658
6659 static int getInvisibleFromInvisibleActiveElement(int element)
6660 {
6661   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6662           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6663           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6664           element);
6665 }
6666
6667 static void RedrawAllLightSwitchesAndInvisibleElements()
6668 {
6669   int x, y;
6670
6671   SCAN_PLAYFIELD(x, y)
6672   {
6673     int element = Feld[x][y];
6674
6675     if (element == EL_LIGHT_SWITCH &&
6676         game.light_time_left > 0)
6677     {
6678       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6679       TEST_DrawLevelField(x, y);
6680     }
6681     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6682              game.light_time_left == 0)
6683     {
6684       Feld[x][y] = EL_LIGHT_SWITCH;
6685       TEST_DrawLevelField(x, y);
6686     }
6687     else if (element == EL_EMC_DRIPPER &&
6688              game.light_time_left > 0)
6689     {
6690       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6691       TEST_DrawLevelField(x, y);
6692     }
6693     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6694              game.light_time_left == 0)
6695     {
6696       Feld[x][y] = EL_EMC_DRIPPER;
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (element == EL_INVISIBLE_STEELWALL ||
6700              element == EL_INVISIBLE_WALL ||
6701              element == EL_INVISIBLE_SAND)
6702     {
6703       if (game.light_time_left > 0)
6704         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6705
6706       TEST_DrawLevelField(x, y);
6707
6708       /* uncrumble neighbour fields, if needed */
6709       if (element == EL_INVISIBLE_SAND)
6710         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6711     }
6712     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6713              element == EL_INVISIBLE_WALL_ACTIVE ||
6714              element == EL_INVISIBLE_SAND_ACTIVE)
6715     {
6716       if (game.light_time_left == 0)
6717         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6718
6719       TEST_DrawLevelField(x, y);
6720
6721       /* re-crumble neighbour fields, if needed */
6722       if (element == EL_INVISIBLE_SAND)
6723         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6724     }
6725   }
6726 }
6727
6728 static void RedrawAllInvisibleElementsForLenses()
6729 {
6730   int x, y;
6731
6732   SCAN_PLAYFIELD(x, y)
6733   {
6734     int element = Feld[x][y];
6735
6736     if (element == EL_EMC_DRIPPER &&
6737         game.lenses_time_left > 0)
6738     {
6739       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6740       TEST_DrawLevelField(x, y);
6741     }
6742     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6743              game.lenses_time_left == 0)
6744     {
6745       Feld[x][y] = EL_EMC_DRIPPER;
6746       TEST_DrawLevelField(x, y);
6747     }
6748     else if (element == EL_INVISIBLE_STEELWALL ||
6749              element == EL_INVISIBLE_WALL ||
6750              element == EL_INVISIBLE_SAND)
6751     {
6752       if (game.lenses_time_left > 0)
6753         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6754
6755       TEST_DrawLevelField(x, y);
6756
6757       /* uncrumble neighbour fields, if needed */
6758       if (element == EL_INVISIBLE_SAND)
6759         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6760     }
6761     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6762              element == EL_INVISIBLE_WALL_ACTIVE ||
6763              element == EL_INVISIBLE_SAND_ACTIVE)
6764     {
6765       if (game.lenses_time_left == 0)
6766         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6767
6768       TEST_DrawLevelField(x, y);
6769
6770       /* re-crumble neighbour fields, if needed */
6771       if (element == EL_INVISIBLE_SAND)
6772         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6773     }
6774   }
6775 }
6776
6777 static void RedrawAllInvisibleElementsForMagnifier()
6778 {
6779   int x, y;
6780
6781   SCAN_PLAYFIELD(x, y)
6782   {
6783     int element = Feld[x][y];
6784
6785     if (element == EL_EMC_FAKE_GRASS &&
6786         game.magnify_time_left > 0)
6787     {
6788       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6789       TEST_DrawLevelField(x, y);
6790     }
6791     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6792              game.magnify_time_left == 0)
6793     {
6794       Feld[x][y] = EL_EMC_FAKE_GRASS;
6795       TEST_DrawLevelField(x, y);
6796     }
6797     else if (IS_GATE_GRAY(element) &&
6798              game.magnify_time_left > 0)
6799     {
6800       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6801                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6802                     IS_EM_GATE_GRAY(element) ?
6803                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6804                     IS_EMC_GATE_GRAY(element) ?
6805                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6806                     IS_DC_GATE_GRAY(element) ?
6807                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6808                     element);
6809       TEST_DrawLevelField(x, y);
6810     }
6811     else if (IS_GATE_GRAY_ACTIVE(element) &&
6812              game.magnify_time_left == 0)
6813     {
6814       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6815                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6816                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6817                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6818                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6819                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6820                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6821                     EL_DC_GATE_WHITE_GRAY :
6822                     element);
6823       TEST_DrawLevelField(x, y);
6824     }
6825   }
6826 }
6827
6828 static void ToggleLightSwitch(int x, int y)
6829 {
6830   int element = Feld[x][y];
6831
6832   game.light_time_left =
6833     (element == EL_LIGHT_SWITCH ?
6834      level.time_light * FRAMES_PER_SECOND : 0);
6835
6836   RedrawAllLightSwitchesAndInvisibleElements();
6837 }
6838
6839 static void ActivateTimegateSwitch(int x, int y)
6840 {
6841   int xx, yy;
6842
6843   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6844
6845   SCAN_PLAYFIELD(xx, yy)
6846   {
6847     int element = Feld[xx][yy];
6848
6849     if (element == EL_TIMEGATE_CLOSED ||
6850         element == EL_TIMEGATE_CLOSING)
6851     {
6852       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6853       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6854     }
6855
6856     /*
6857     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6858     {
6859       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6860       TEST_DrawLevelField(xx, yy);
6861     }
6862     */
6863
6864   }
6865
6866 #if 1
6867   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6868                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6869 #else
6870   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6871 #endif
6872 }
6873
6874 void Impact(int x, int y)
6875 {
6876   boolean last_line = (y == lev_fieldy - 1);
6877   boolean object_hit = FALSE;
6878   boolean impact = (last_line || object_hit);
6879   int element = Feld[x][y];
6880   int smashed = EL_STEELWALL;
6881
6882   if (!last_line)       /* check if element below was hit */
6883   {
6884     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6885       return;
6886
6887     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6888                                          MovDir[x][y + 1] != MV_DOWN ||
6889                                          MovPos[x][y + 1] <= TILEY / 2));
6890
6891     /* do not smash moving elements that left the smashed field in time */
6892     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6893         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6894       object_hit = FALSE;
6895
6896 #if USE_QUICKSAND_IMPACT_BUGFIX
6897     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6898     {
6899       RemoveMovingField(x, y + 1);
6900       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6901       Feld[x][y + 2] = EL_ROCK;
6902       TEST_DrawLevelField(x, y + 2);
6903
6904       object_hit = TRUE;
6905     }
6906
6907     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6908     {
6909       RemoveMovingField(x, y + 1);
6910       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6911       Feld[x][y + 2] = EL_ROCK;
6912       TEST_DrawLevelField(x, y + 2);
6913
6914       object_hit = TRUE;
6915     }
6916 #endif
6917
6918     if (object_hit)
6919       smashed = MovingOrBlocked2Element(x, y + 1);
6920
6921     impact = (last_line || object_hit);
6922   }
6923
6924   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6925   {
6926     SplashAcid(x, y + 1);
6927     return;
6928   }
6929
6930   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6931   /* only reset graphic animation if graphic really changes after impact */
6932   if (impact &&
6933       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6934   {
6935     ResetGfxAnimation(x, y);
6936     TEST_DrawLevelField(x, y);
6937   }
6938
6939   if (impact && CAN_EXPLODE_IMPACT(element))
6940   {
6941     Bang(x, y);
6942     return;
6943   }
6944   else if (impact && element == EL_PEARL &&
6945            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6946   {
6947     ResetGfxAnimation(x, y);
6948
6949     Feld[x][y] = EL_PEARL_BREAKING;
6950     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6951     return;
6952   }
6953   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6954   {
6955     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6956
6957     return;
6958   }
6959
6960   if (impact && element == EL_AMOEBA_DROP)
6961   {
6962     if (object_hit && IS_PLAYER(x, y + 1))
6963       KillPlayerUnlessEnemyProtected(x, y + 1);
6964     else if (object_hit && smashed == EL_PENGUIN)
6965       Bang(x, y + 1);
6966     else
6967     {
6968       Feld[x][y] = EL_AMOEBA_GROWING;
6969       Store[x][y] = EL_AMOEBA_WET;
6970
6971       ResetRandomAnimationValue(x, y);
6972     }
6973     return;
6974   }
6975
6976   if (object_hit)               /* check which object was hit */
6977   {
6978     if ((CAN_PASS_MAGIC_WALL(element) && 
6979          (smashed == EL_MAGIC_WALL ||
6980           smashed == EL_BD_MAGIC_WALL)) ||
6981         (CAN_PASS_DC_MAGIC_WALL(element) &&
6982          smashed == EL_DC_MAGIC_WALL))
6983     {
6984       int xx, yy;
6985       int activated_magic_wall =
6986         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6987          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6988          EL_DC_MAGIC_WALL_ACTIVE);
6989
6990       /* activate magic wall / mill */
6991       SCAN_PLAYFIELD(xx, yy)
6992       {
6993         if (Feld[xx][yy] == smashed)
6994           Feld[xx][yy] = activated_magic_wall;
6995       }
6996
6997       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6998       game.magic_wall_active = TRUE;
6999
7000       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7001                             SND_MAGIC_WALL_ACTIVATING :
7002                             smashed == EL_BD_MAGIC_WALL ?
7003                             SND_BD_MAGIC_WALL_ACTIVATING :
7004                             SND_DC_MAGIC_WALL_ACTIVATING));
7005     }
7006
7007     if (IS_PLAYER(x, y + 1))
7008     {
7009       if (CAN_SMASH_PLAYER(element))
7010       {
7011         KillPlayerUnlessEnemyProtected(x, y + 1);
7012         return;
7013       }
7014     }
7015     else if (smashed == EL_PENGUIN)
7016     {
7017       if (CAN_SMASH_PLAYER(element))
7018       {
7019         Bang(x, y + 1);
7020         return;
7021       }
7022     }
7023     else if (element == EL_BD_DIAMOND)
7024     {
7025       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7026       {
7027         Bang(x, y + 1);
7028         return;
7029       }
7030     }
7031     else if (((element == EL_SP_INFOTRON ||
7032                element == EL_SP_ZONK) &&
7033               (smashed == EL_SP_SNIKSNAK ||
7034                smashed == EL_SP_ELECTRON ||
7035                smashed == EL_SP_DISK_ORANGE)) ||
7036              (element == EL_SP_INFOTRON &&
7037               smashed == EL_SP_DISK_YELLOW))
7038     {
7039       Bang(x, y + 1);
7040       return;
7041     }
7042     else if (CAN_SMASH_EVERYTHING(element))
7043     {
7044       if (IS_CLASSIC_ENEMY(smashed) ||
7045           CAN_EXPLODE_SMASHED(smashed))
7046       {
7047         Bang(x, y + 1);
7048         return;
7049       }
7050       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7051       {
7052         if (smashed == EL_LAMP ||
7053             smashed == EL_LAMP_ACTIVE)
7054         {
7055           Bang(x, y + 1);
7056           return;
7057         }
7058         else if (smashed == EL_NUT)
7059         {
7060           Feld[x][y + 1] = EL_NUT_BREAKING;
7061           PlayLevelSound(x, y, SND_NUT_BREAKING);
7062           RaiseScoreElement(EL_NUT);
7063           return;
7064         }
7065         else if (smashed == EL_PEARL)
7066         {
7067           ResetGfxAnimation(x, y);
7068
7069           Feld[x][y + 1] = EL_PEARL_BREAKING;
7070           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7071           return;
7072         }
7073         else if (smashed == EL_DIAMOND)
7074         {
7075           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7076           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7077           return;
7078         }
7079         else if (IS_BELT_SWITCH(smashed))
7080         {
7081           ToggleBeltSwitch(x, y + 1);
7082         }
7083         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7084                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7085                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7086                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7087         {
7088           ToggleSwitchgateSwitch(x, y + 1);
7089         }
7090         else if (smashed == EL_LIGHT_SWITCH ||
7091                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7092         {
7093           ToggleLightSwitch(x, y + 1);
7094         }
7095         else
7096         {
7097 #if 0
7098           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7099 #endif
7100
7101           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7102
7103           CheckElementChangeBySide(x, y + 1, smashed, element,
7104                                    CE_SWITCHED, CH_SIDE_TOP);
7105           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7106                                             CH_SIDE_TOP);
7107         }
7108       }
7109       else
7110       {
7111         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7112       }
7113     }
7114   }
7115
7116   /* play sound of magic wall / mill */
7117   if (!last_line &&
7118       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7119        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7120        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7121   {
7122     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7123       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7124     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7125       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7126     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7127       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7128
7129     return;
7130   }
7131
7132   /* play sound of object that hits the ground */
7133   if (last_line || object_hit)
7134     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7135 }
7136
7137 inline static void TurnRoundExt(int x, int y)
7138 {
7139   static struct
7140   {
7141     int dx, dy;
7142   } move_xy[] =
7143   {
7144     {  0,  0 },
7145     { -1,  0 },
7146     { +1,  0 },
7147     {  0,  0 },
7148     {  0, -1 },
7149     {  0,  0 }, { 0, 0 }, { 0, 0 },
7150     {  0, +1 }
7151   };
7152   static struct
7153   {
7154     int left, right, back;
7155   } turn[] =
7156   {
7157     { 0,        0,              0        },
7158     { MV_DOWN,  MV_UP,          MV_RIGHT },
7159     { MV_UP,    MV_DOWN,        MV_LEFT  },
7160     { 0,        0,              0        },
7161     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7162     { 0,        0,              0        },
7163     { 0,        0,              0        },
7164     { 0,        0,              0        },
7165     { MV_RIGHT, MV_LEFT,        MV_UP    }
7166   };
7167
7168   int element = Feld[x][y];
7169   int move_pattern = element_info[element].move_pattern;
7170
7171   int old_move_dir = MovDir[x][y];
7172   int left_dir  = turn[old_move_dir].left;
7173   int right_dir = turn[old_move_dir].right;
7174   int back_dir  = turn[old_move_dir].back;
7175
7176   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7177   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7178   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7179   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7180
7181   int left_x  = x + left_dx,  left_y  = y + left_dy;
7182   int right_x = x + right_dx, right_y = y + right_dy;
7183   int move_x  = x + move_dx,  move_y  = y + move_dy;
7184
7185   int xx, yy;
7186
7187   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7188   {
7189     TestIfBadThingTouchesOtherBadThing(x, y);
7190
7191     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7192       MovDir[x][y] = right_dir;
7193     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7194       MovDir[x][y] = left_dir;
7195
7196     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7197       MovDelay[x][y] = 9;
7198     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7199       MovDelay[x][y] = 1;
7200   }
7201   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7202   {
7203     TestIfBadThingTouchesOtherBadThing(x, y);
7204
7205     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7206       MovDir[x][y] = left_dir;
7207     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7208       MovDir[x][y] = right_dir;
7209
7210     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7211       MovDelay[x][y] = 9;
7212     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7213       MovDelay[x][y] = 1;
7214   }
7215   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7216   {
7217     TestIfBadThingTouchesOtherBadThing(x, y);
7218
7219     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7220       MovDir[x][y] = left_dir;
7221     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7222       MovDir[x][y] = right_dir;
7223
7224     if (MovDir[x][y] != old_move_dir)
7225       MovDelay[x][y] = 9;
7226   }
7227   else if (element == EL_YAMYAM)
7228   {
7229     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7230     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7231
7232     if (can_turn_left && can_turn_right)
7233       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7234     else if (can_turn_left)
7235       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7236     else if (can_turn_right)
7237       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7238     else
7239       MovDir[x][y] = back_dir;
7240
7241     MovDelay[x][y] = 16 + 16 * RND(3);
7242   }
7243   else if (element == EL_DARK_YAMYAM)
7244   {
7245     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7246                                                          left_x, left_y);
7247     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7248                                                          right_x, right_y);
7249
7250     if (can_turn_left && can_turn_right)
7251       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7252     else if (can_turn_left)
7253       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7254     else if (can_turn_right)
7255       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7256     else
7257       MovDir[x][y] = back_dir;
7258
7259     MovDelay[x][y] = 16 + 16 * RND(3);
7260   }
7261   else if (element == EL_PACMAN)
7262   {
7263     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7264     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7265
7266     if (can_turn_left && can_turn_right)
7267       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7268     else if (can_turn_left)
7269       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7270     else if (can_turn_right)
7271       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7272     else
7273       MovDir[x][y] = back_dir;
7274
7275     MovDelay[x][y] = 6 + RND(40);
7276   }
7277   else if (element == EL_PIG)
7278   {
7279     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7280     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7281     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7282     boolean should_turn_left, should_turn_right, should_move_on;
7283     int rnd_value = 24;
7284     int rnd = RND(rnd_value);
7285
7286     should_turn_left = (can_turn_left &&
7287                         (!can_move_on ||
7288                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7289                                                    y + back_dy + left_dy)));
7290     should_turn_right = (can_turn_right &&
7291                          (!can_move_on ||
7292                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7293                                                     y + back_dy + right_dy)));
7294     should_move_on = (can_move_on &&
7295                       (!can_turn_left ||
7296                        !can_turn_right ||
7297                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7298                                                  y + move_dy + left_dy) ||
7299                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7300                                                  y + move_dy + right_dy)));
7301
7302     if (should_turn_left || should_turn_right || should_move_on)
7303     {
7304       if (should_turn_left && should_turn_right && should_move_on)
7305         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7306                         rnd < 2 * rnd_value / 3 ? right_dir :
7307                         old_move_dir);
7308       else if (should_turn_left && should_turn_right)
7309         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7310       else if (should_turn_left && should_move_on)
7311         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7312       else if (should_turn_right && should_move_on)
7313         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7314       else if (should_turn_left)
7315         MovDir[x][y] = left_dir;
7316       else if (should_turn_right)
7317         MovDir[x][y] = right_dir;
7318       else if (should_move_on)
7319         MovDir[x][y] = old_move_dir;
7320     }
7321     else if (can_move_on && rnd > rnd_value / 8)
7322       MovDir[x][y] = old_move_dir;
7323     else if (can_turn_left && can_turn_right)
7324       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325     else if (can_turn_left && rnd > rnd_value / 8)
7326       MovDir[x][y] = left_dir;
7327     else if (can_turn_right && rnd > rnd_value/8)
7328       MovDir[x][y] = right_dir;
7329     else
7330       MovDir[x][y] = back_dir;
7331
7332     xx = x + move_xy[MovDir[x][y]].dx;
7333     yy = y + move_xy[MovDir[x][y]].dy;
7334
7335     if (!IN_LEV_FIELD(xx, yy) ||
7336         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7337       MovDir[x][y] = old_move_dir;
7338
7339     MovDelay[x][y] = 0;
7340   }
7341   else if (element == EL_DRAGON)
7342   {
7343     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7344     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7345     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7346     int rnd_value = 24;
7347     int rnd = RND(rnd_value);
7348
7349     if (can_move_on && rnd > rnd_value / 8)
7350       MovDir[x][y] = old_move_dir;
7351     else if (can_turn_left && can_turn_right)
7352       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7353     else if (can_turn_left && rnd > rnd_value / 8)
7354       MovDir[x][y] = left_dir;
7355     else if (can_turn_right && rnd > rnd_value / 8)
7356       MovDir[x][y] = right_dir;
7357     else
7358       MovDir[x][y] = back_dir;
7359
7360     xx = x + move_xy[MovDir[x][y]].dx;
7361     yy = y + move_xy[MovDir[x][y]].dy;
7362
7363     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7364       MovDir[x][y] = old_move_dir;
7365
7366     MovDelay[x][y] = 0;
7367   }
7368   else if (element == EL_MOLE)
7369   {
7370     boolean can_move_on =
7371       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7372                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7373                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7374     if (!can_move_on)
7375     {
7376       boolean can_turn_left =
7377         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7378                               IS_AMOEBOID(Feld[left_x][left_y])));
7379
7380       boolean can_turn_right =
7381         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7382                               IS_AMOEBOID(Feld[right_x][right_y])));
7383
7384       if (can_turn_left && can_turn_right)
7385         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7386       else if (can_turn_left)
7387         MovDir[x][y] = left_dir;
7388       else
7389         MovDir[x][y] = right_dir;
7390     }
7391
7392     if (MovDir[x][y] != old_move_dir)
7393       MovDelay[x][y] = 9;
7394   }
7395   else if (element == EL_BALLOON)
7396   {
7397     MovDir[x][y] = game.wind_direction;
7398     MovDelay[x][y] = 0;
7399   }
7400   else if (element == EL_SPRING)
7401   {
7402 #if USE_NEW_SPRING_BUMPER
7403     if (MovDir[x][y] & MV_HORIZONTAL)
7404     {
7405       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7406           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7407       {
7408         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7409         ResetGfxAnimation(move_x, move_y);
7410         TEST_DrawLevelField(move_x, move_y);
7411
7412         MovDir[x][y] = back_dir;
7413       }
7414       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7415                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7416         MovDir[x][y] = MV_NONE;
7417     }
7418 #else
7419     if (MovDir[x][y] & MV_HORIZONTAL &&
7420         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7421          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7422       MovDir[x][y] = MV_NONE;
7423 #endif
7424
7425     MovDelay[x][y] = 0;
7426   }
7427   else if (element == EL_ROBOT ||
7428            element == EL_SATELLITE ||
7429            element == EL_PENGUIN ||
7430            element == EL_EMC_ANDROID)
7431   {
7432     int attr_x = -1, attr_y = -1;
7433
7434     if (AllPlayersGone)
7435     {
7436       attr_x = ExitX;
7437       attr_y = ExitY;
7438     }
7439     else
7440     {
7441       int i;
7442
7443       for (i = 0; i < MAX_PLAYERS; i++)
7444       {
7445         struct PlayerInfo *player = &stored_player[i];
7446         int jx = player->jx, jy = player->jy;
7447
7448         if (!player->active)
7449           continue;
7450
7451         if (attr_x == -1 ||
7452             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7453         {
7454           attr_x = jx;
7455           attr_y = jy;
7456         }
7457       }
7458     }
7459
7460     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7461         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7462          game.engine_version < VERSION_IDENT(3,1,0,0)))
7463     {
7464       attr_x = ZX;
7465       attr_y = ZY;
7466     }
7467
7468     if (element == EL_PENGUIN)
7469     {
7470       int i;
7471       static int xy[4][2] =
7472       {
7473         { 0, -1 },
7474         { -1, 0 },
7475         { +1, 0 },
7476         { 0, +1 }
7477       };
7478
7479       for (i = 0; i < NUM_DIRECTIONS; i++)
7480       {
7481         int ex = x + xy[i][0];
7482         int ey = y + xy[i][1];
7483
7484         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7485                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7486                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7487                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7488         {
7489           attr_x = ex;
7490           attr_y = ey;
7491           break;
7492         }
7493       }
7494     }
7495
7496     MovDir[x][y] = MV_NONE;
7497     if (attr_x < x)
7498       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7499     else if (attr_x > x)
7500       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7501     if (attr_y < y)
7502       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7503     else if (attr_y > y)
7504       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7505
7506     if (element == EL_ROBOT)
7507     {
7508       int newx, newy;
7509
7510       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7511         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7512       Moving2Blocked(x, y, &newx, &newy);
7513
7514       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7515         MovDelay[x][y] = 8 + 8 * !RND(3);
7516       else
7517         MovDelay[x][y] = 16;
7518     }
7519     else if (element == EL_PENGUIN)
7520     {
7521       int newx, newy;
7522
7523       MovDelay[x][y] = 1;
7524
7525       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526       {
7527         boolean first_horiz = RND(2);
7528         int new_move_dir = MovDir[x][y];
7529
7530         MovDir[x][y] =
7531           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7532         Moving2Blocked(x, y, &newx, &newy);
7533
7534         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7535           return;
7536
7537         MovDir[x][y] =
7538           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7539         Moving2Blocked(x, y, &newx, &newy);
7540
7541         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7542           return;
7543
7544         MovDir[x][y] = old_move_dir;
7545         return;
7546       }
7547     }
7548     else if (element == EL_SATELLITE)
7549     {
7550       int newx, newy;
7551
7552       MovDelay[x][y] = 1;
7553
7554       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7555       {
7556         boolean first_horiz = RND(2);
7557         int new_move_dir = MovDir[x][y];
7558
7559         MovDir[x][y] =
7560           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7561         Moving2Blocked(x, y, &newx, &newy);
7562
7563         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7564           return;
7565
7566         MovDir[x][y] =
7567           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7568         Moving2Blocked(x, y, &newx, &newy);
7569
7570         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7571           return;
7572
7573         MovDir[x][y] = old_move_dir;
7574         return;
7575       }
7576     }
7577     else if (element == EL_EMC_ANDROID)
7578     {
7579       static int check_pos[16] =
7580       {
7581         -1,             /*  0 => (invalid)          */
7582         7,              /*  1 => MV_LEFT            */
7583         3,              /*  2 => MV_RIGHT           */
7584         -1,             /*  3 => (invalid)          */
7585         1,              /*  4 =>            MV_UP   */
7586         0,              /*  5 => MV_LEFT  | MV_UP   */
7587         2,              /*  6 => MV_RIGHT | MV_UP   */
7588         -1,             /*  7 => (invalid)          */
7589         5,              /*  8 =>            MV_DOWN */
7590         6,              /*  9 => MV_LEFT  | MV_DOWN */
7591         4,              /* 10 => MV_RIGHT | MV_DOWN */
7592         -1,             /* 11 => (invalid)          */
7593         -1,             /* 12 => (invalid)          */
7594         -1,             /* 13 => (invalid)          */
7595         -1,             /* 14 => (invalid)          */
7596         -1,             /* 15 => (invalid)          */
7597       };
7598       static struct
7599       {
7600         int dx, dy;
7601         int dir;
7602       } check_xy[8] =
7603       {
7604         { -1, -1,       MV_LEFT  | MV_UP   },
7605         {  0, -1,                  MV_UP   },
7606         { +1, -1,       MV_RIGHT | MV_UP   },
7607         { +1,  0,       MV_RIGHT           },
7608         { +1, +1,       MV_RIGHT | MV_DOWN },
7609         {  0, +1,                  MV_DOWN },
7610         { -1, +1,       MV_LEFT  | MV_DOWN },
7611         { -1,  0,       MV_LEFT            },
7612       };
7613       int start_pos, check_order;
7614       boolean can_clone = FALSE;
7615       int i;
7616
7617       /* check if there is any free field around current position */
7618       for (i = 0; i < 8; i++)
7619       {
7620         int newx = x + check_xy[i].dx;
7621         int newy = y + check_xy[i].dy;
7622
7623         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7624         {
7625           can_clone = TRUE;
7626
7627           break;
7628         }
7629       }
7630
7631       if (can_clone)            /* randomly find an element to clone */
7632       {
7633         can_clone = FALSE;
7634
7635         start_pos = check_pos[RND(8)];
7636         check_order = (RND(2) ? -1 : +1);
7637
7638         for (i = 0; i < 8; i++)
7639         {
7640           int pos_raw = start_pos + i * check_order;
7641           int pos = (pos_raw + 8) % 8;
7642           int newx = x + check_xy[pos].dx;
7643           int newy = y + check_xy[pos].dy;
7644
7645           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7646           {
7647             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7648             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7649
7650             Store[x][y] = Feld[newx][newy];
7651
7652             can_clone = TRUE;
7653
7654             break;
7655           }
7656         }
7657       }
7658
7659       if (can_clone)            /* randomly find a direction to move */
7660       {
7661         can_clone = FALSE;
7662
7663         start_pos = check_pos[RND(8)];
7664         check_order = (RND(2) ? -1 : +1);
7665
7666         for (i = 0; i < 8; i++)
7667         {
7668           int pos_raw = start_pos + i * check_order;
7669           int pos = (pos_raw + 8) % 8;
7670           int newx = x + check_xy[pos].dx;
7671           int newy = y + check_xy[pos].dy;
7672           int new_move_dir = check_xy[pos].dir;
7673
7674           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7675           {
7676             MovDir[x][y] = new_move_dir;
7677             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7678
7679             can_clone = TRUE;
7680
7681             break;
7682           }
7683         }
7684       }
7685
7686       if (can_clone)            /* cloning and moving successful */
7687         return;
7688
7689       /* cannot clone -- try to move towards player */
7690
7691       start_pos = check_pos[MovDir[x][y] & 0x0f];
7692       check_order = (RND(2) ? -1 : +1);
7693
7694       for (i = 0; i < 3; i++)
7695       {
7696         /* first check start_pos, then previous/next or (next/previous) pos */
7697         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7698         int pos = (pos_raw + 8) % 8;
7699         int newx = x + check_xy[pos].dx;
7700         int newy = y + check_xy[pos].dy;
7701         int new_move_dir = check_xy[pos].dir;
7702
7703         if (IS_PLAYER(newx, newy))
7704           break;
7705
7706         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7707         {
7708           MovDir[x][y] = new_move_dir;
7709           MovDelay[x][y] = level.android_move_time * 8 + 1;
7710
7711           break;
7712         }
7713       }
7714     }
7715   }
7716   else if (move_pattern == MV_TURNING_LEFT ||
7717            move_pattern == MV_TURNING_RIGHT ||
7718            move_pattern == MV_TURNING_LEFT_RIGHT ||
7719            move_pattern == MV_TURNING_RIGHT_LEFT ||
7720            move_pattern == MV_TURNING_RANDOM ||
7721            move_pattern == MV_ALL_DIRECTIONS)
7722   {
7723     boolean can_turn_left =
7724       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7725     boolean can_turn_right =
7726       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7727
7728     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7729       return;
7730
7731     if (move_pattern == MV_TURNING_LEFT)
7732       MovDir[x][y] = left_dir;
7733     else if (move_pattern == MV_TURNING_RIGHT)
7734       MovDir[x][y] = right_dir;
7735     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7736       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7737     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7738       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7739     else if (move_pattern == MV_TURNING_RANDOM)
7740       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7741                       can_turn_right && !can_turn_left ? right_dir :
7742                       RND(2) ? left_dir : right_dir);
7743     else if (can_turn_left && can_turn_right)
7744       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7745     else if (can_turn_left)
7746       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7747     else if (can_turn_right)
7748       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7749     else
7750       MovDir[x][y] = back_dir;
7751
7752     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7753   }
7754   else if (move_pattern == MV_HORIZONTAL ||
7755            move_pattern == MV_VERTICAL)
7756   {
7757     if (move_pattern & old_move_dir)
7758       MovDir[x][y] = back_dir;
7759     else if (move_pattern == MV_HORIZONTAL)
7760       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7761     else if (move_pattern == MV_VERTICAL)
7762       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7763
7764     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7765   }
7766   else if (move_pattern & MV_ANY_DIRECTION)
7767   {
7768     MovDir[x][y] = move_pattern;
7769     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7770   }
7771   else if (move_pattern & MV_WIND_DIRECTION)
7772   {
7773     MovDir[x][y] = game.wind_direction;
7774     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7775   }
7776   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7777   {
7778     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7779       MovDir[x][y] = left_dir;
7780     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7781       MovDir[x][y] = right_dir;
7782
7783     if (MovDir[x][y] != old_move_dir)
7784       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7787   {
7788     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7789       MovDir[x][y] = right_dir;
7790     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7791       MovDir[x][y] = left_dir;
7792
7793     if (MovDir[x][y] != old_move_dir)
7794       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7795   }
7796   else if (move_pattern == MV_TOWARDS_PLAYER ||
7797            move_pattern == MV_AWAY_FROM_PLAYER)
7798   {
7799     int attr_x = -1, attr_y = -1;
7800     int newx, newy;
7801     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7802
7803     if (AllPlayersGone)
7804     {
7805       attr_x = ExitX;
7806       attr_y = ExitY;
7807     }
7808     else
7809     {
7810       int i;
7811
7812       for (i = 0; i < MAX_PLAYERS; i++)
7813       {
7814         struct PlayerInfo *player = &stored_player[i];
7815         int jx = player->jx, jy = player->jy;
7816
7817         if (!player->active)
7818           continue;
7819
7820         if (attr_x == -1 ||
7821             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7822         {
7823           attr_x = jx;
7824           attr_y = jy;
7825         }
7826       }
7827     }
7828
7829     MovDir[x][y] = MV_NONE;
7830     if (attr_x < x)
7831       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7832     else if (attr_x > x)
7833       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7834     if (attr_y < y)
7835       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7836     else if (attr_y > y)
7837       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7838
7839     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7840
7841     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7842     {
7843       boolean first_horiz = RND(2);
7844       int new_move_dir = MovDir[x][y];
7845
7846       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7847       {
7848         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7849         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7850
7851         return;
7852       }
7853
7854       MovDir[x][y] =
7855         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7856       Moving2Blocked(x, y, &newx, &newy);
7857
7858       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7859         return;
7860
7861       MovDir[x][y] =
7862         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7863       Moving2Blocked(x, y, &newx, &newy);
7864
7865       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7866         return;
7867
7868       MovDir[x][y] = old_move_dir;
7869     }
7870   }
7871   else if (move_pattern == MV_WHEN_PUSHED ||
7872            move_pattern == MV_WHEN_DROPPED)
7873   {
7874     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7875       MovDir[x][y] = MV_NONE;
7876
7877     MovDelay[x][y] = 0;
7878   }
7879   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7880   {
7881     static int test_xy[7][2] =
7882     {
7883       { 0, -1 },
7884       { -1, 0 },
7885       { +1, 0 },
7886       { 0, +1 },
7887       { 0, -1 },
7888       { -1, 0 },
7889       { +1, 0 },
7890     };
7891     static int test_dir[7] =
7892     {
7893       MV_UP,
7894       MV_LEFT,
7895       MV_RIGHT,
7896       MV_DOWN,
7897       MV_UP,
7898       MV_LEFT,
7899       MV_RIGHT,
7900     };
7901     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7902     int move_preference = -1000000;     /* start with very low preference */
7903     int new_move_dir = MV_NONE;
7904     int start_test = RND(4);
7905     int i;
7906
7907     for (i = 0; i < NUM_DIRECTIONS; i++)
7908     {
7909       int move_dir = test_dir[start_test + i];
7910       int move_dir_preference;
7911
7912       xx = x + test_xy[start_test + i][0];
7913       yy = y + test_xy[start_test + i][1];
7914
7915       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7916           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7917       {
7918         new_move_dir = move_dir;
7919
7920         break;
7921       }
7922
7923       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7924         continue;
7925
7926       move_dir_preference = -1 * RunnerVisit[xx][yy];
7927       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7928         move_dir_preference = PlayerVisit[xx][yy];
7929
7930       if (move_dir_preference > move_preference)
7931       {
7932         /* prefer field that has not been visited for the longest time */
7933         move_preference = move_dir_preference;
7934         new_move_dir = move_dir;
7935       }
7936       else if (move_dir_preference == move_preference &&
7937                move_dir == old_move_dir)
7938       {
7939         /* prefer last direction when all directions are preferred equally */
7940         move_preference = move_dir_preference;
7941         new_move_dir = move_dir;
7942       }
7943     }
7944
7945     MovDir[x][y] = new_move_dir;
7946     if (old_move_dir != new_move_dir)
7947       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7948   }
7949 }
7950
7951 static void TurnRound(int x, int y)
7952 {
7953   int direction = MovDir[x][y];
7954
7955   TurnRoundExt(x, y);
7956
7957   GfxDir[x][y] = MovDir[x][y];
7958
7959   if (direction != MovDir[x][y])
7960     GfxFrame[x][y] = 0;
7961
7962   if (MovDelay[x][y])
7963     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7964
7965   ResetGfxFrame(x, y, FALSE);
7966 }
7967
7968 static boolean JustBeingPushed(int x, int y)
7969 {
7970   int i;
7971
7972   for (i = 0; i < MAX_PLAYERS; i++)
7973   {
7974     struct PlayerInfo *player = &stored_player[i];
7975
7976     if (player->active && player->is_pushing && player->MovPos)
7977     {
7978       int next_jx = player->jx + (player->jx - player->last_jx);
7979       int next_jy = player->jy + (player->jy - player->last_jy);
7980
7981       if (x == next_jx && y == next_jy)
7982         return TRUE;
7983     }
7984   }
7985
7986   return FALSE;
7987 }
7988
7989 void StartMoving(int x, int y)
7990 {
7991   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7992   int element = Feld[x][y];
7993
7994   if (Stop[x][y])
7995     return;
7996
7997   if (MovDelay[x][y] == 0)
7998     GfxAction[x][y] = ACTION_DEFAULT;
7999
8000   if (CAN_FALL(element) && y < lev_fieldy - 1)
8001   {
8002     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8003         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8004       if (JustBeingPushed(x, y))
8005         return;
8006
8007     if (element == EL_QUICKSAND_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_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_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_EMPTYING);
8037           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8038
8039           MovDelay[x][y]--;
8040           if (MovDelay[x][y])
8041             return;
8042         }
8043
8044         Feld[x][y] = EL_QUICKSAND_EMPTY;
8045         Feld[x][y + 1] = EL_QUICKSAND_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_FAST_EMPTY)
8052       {
8053         if (!MovDelay[x][y])
8054         {
8055           MovDelay[x][y] = TILEY + 1;
8056
8057           ResetGfxAnimation(x, y);
8058           ResetGfxAnimation(x, y + 1);
8059         }
8060
8061         if (MovDelay[x][y])
8062         {
8063           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8064           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8065
8066           MovDelay[x][y]--;
8067           if (MovDelay[x][y])
8068             return;
8069         }
8070
8071         Feld[x][y] = EL_QUICKSAND_EMPTY;
8072         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8073         Store[x][y + 1] = Store[x][y];
8074         Store[x][y] = 0;
8075
8076         PlayLevelSoundAction(x, y, ACTION_FILLING);
8077       }
8078     }
8079     else if (element == EL_QUICKSAND_FAST_FULL)
8080     {
8081       if (IS_FREE(x, y + 1))
8082       {
8083         InitMovingField(x, y, MV_DOWN);
8084         started_moving = TRUE;
8085
8086         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8087 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8088         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8089           Store[x][y] = EL_ROCK;
8090 #else
8091         Store[x][y] = EL_ROCK;
8092 #endif
8093
8094         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8095       }
8096       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8097       {
8098         if (!MovDelay[x][y])
8099         {
8100           MovDelay[x][y] = TILEY + 1;
8101
8102           ResetGfxAnimation(x, y);
8103           ResetGfxAnimation(x, y + 1);
8104         }
8105
8106         if (MovDelay[x][y])
8107         {
8108           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8109           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8110
8111           MovDelay[x][y]--;
8112           if (MovDelay[x][y])
8113             return;
8114         }
8115
8116         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8117         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8118         Store[x][y + 1] = Store[x][y];
8119         Store[x][y] = 0;
8120
8121         PlayLevelSoundAction(x, y, ACTION_FILLING);
8122       }
8123       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8124       {
8125         if (!MovDelay[x][y])
8126         {
8127           MovDelay[x][y] = TILEY + 1;
8128
8129           ResetGfxAnimation(x, y);
8130           ResetGfxAnimation(x, y + 1);
8131         }
8132
8133         if (MovDelay[x][y])
8134         {
8135           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8136           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8137
8138           MovDelay[x][y]--;
8139           if (MovDelay[x][y])
8140             return;
8141         }
8142
8143         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8144         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8145         Store[x][y + 1] = Store[x][y];
8146         Store[x][y] = 0;
8147
8148         PlayLevelSoundAction(x, y, ACTION_FILLING);
8149       }
8150     }
8151     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8152              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8153     {
8154       InitMovingField(x, y, MV_DOWN);
8155       started_moving = TRUE;
8156
8157       Feld[x][y] = EL_QUICKSAND_FILLING;
8158       Store[x][y] = element;
8159
8160       PlayLevelSoundAction(x, y, ACTION_FILLING);
8161     }
8162     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8163              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8164     {
8165       InitMovingField(x, y, MV_DOWN);
8166       started_moving = TRUE;
8167
8168       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8169       Store[x][y] = element;
8170
8171       PlayLevelSoundAction(x, y, ACTION_FILLING);
8172     }
8173     else if (element == EL_MAGIC_WALL_FULL)
8174     {
8175       if (IS_FREE(x, y + 1))
8176       {
8177         InitMovingField(x, y, MV_DOWN);
8178         started_moving = TRUE;
8179
8180         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8181         Store[x][y] = EL_CHANGED(Store[x][y]);
8182       }
8183       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8184       {
8185         if (!MovDelay[x][y])
8186           MovDelay[x][y] = TILEY / 4 + 1;
8187
8188         if (MovDelay[x][y])
8189         {
8190           MovDelay[x][y]--;
8191           if (MovDelay[x][y])
8192             return;
8193         }
8194
8195         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8196         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8197         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8198         Store[x][y] = 0;
8199       }
8200     }
8201     else if (element == EL_BD_MAGIC_WALL_FULL)
8202     {
8203       if (IS_FREE(x, y + 1))
8204       {
8205         InitMovingField(x, y, MV_DOWN);
8206         started_moving = TRUE;
8207
8208         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8209         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8210       }
8211       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8212       {
8213         if (!MovDelay[x][y])
8214           MovDelay[x][y] = TILEY / 4 + 1;
8215
8216         if (MovDelay[x][y])
8217         {
8218           MovDelay[x][y]--;
8219           if (MovDelay[x][y])
8220             return;
8221         }
8222
8223         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8224         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8225         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8226         Store[x][y] = 0;
8227       }
8228     }
8229     else if (element == EL_DC_MAGIC_WALL_FULL)
8230     {
8231       if (IS_FREE(x, y + 1))
8232       {
8233         InitMovingField(x, y, MV_DOWN);
8234         started_moving = TRUE;
8235
8236         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8237         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8238       }
8239       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8240       {
8241         if (!MovDelay[x][y])
8242           MovDelay[x][y] = TILEY / 4 + 1;
8243
8244         if (MovDelay[x][y])
8245         {
8246           MovDelay[x][y]--;
8247           if (MovDelay[x][y])
8248             return;
8249         }
8250
8251         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8252         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8253         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8254         Store[x][y] = 0;
8255       }
8256     }
8257     else if ((CAN_PASS_MAGIC_WALL(element) &&
8258               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8259                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8260              (CAN_PASS_DC_MAGIC_WALL(element) &&
8261               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8262
8263     {
8264       InitMovingField(x, y, MV_DOWN);
8265       started_moving = TRUE;
8266
8267       Feld[x][y] =
8268         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8269          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8270          EL_DC_MAGIC_WALL_FILLING);
8271       Store[x][y] = element;
8272     }
8273     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8274     {
8275       SplashAcid(x, y + 1);
8276
8277       InitMovingField(x, y, MV_DOWN);
8278       started_moving = TRUE;
8279
8280       Store[x][y] = EL_ACID;
8281     }
8282     else if (
8283 #if USE_FIX_IMPACT_COLLISION
8284              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8285               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8286 #else
8287              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8288               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8289 #endif
8290              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8291               CAN_FALL(element) && WasJustFalling[x][y] &&
8292               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8293
8294              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8295               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8296               (Feld[x][y + 1] == EL_BLOCKED)))
8297     {
8298       /* this is needed for a special case not covered by calling "Impact()"
8299          from "ContinueMoving()": if an element moves to a tile directly below
8300          another element which was just falling on that tile (which was empty
8301          in the previous frame), the falling element above would just stop
8302          instead of smashing the element below (in previous version, the above
8303          element was just checked for "moving" instead of "falling", resulting
8304          in incorrect smashes caused by horizontal movement of the above
8305          element; also, the case of the player being the element to smash was
8306          simply not covered here... :-/ ) */
8307
8308       CheckCollision[x][y] = 0;
8309       CheckImpact[x][y] = 0;
8310
8311       Impact(x, y);
8312     }
8313     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8314     {
8315       if (MovDir[x][y] == MV_NONE)
8316       {
8317         InitMovingField(x, y, MV_DOWN);
8318         started_moving = TRUE;
8319       }
8320     }
8321     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8322     {
8323       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8324         MovDir[x][y] = MV_DOWN;
8325
8326       InitMovingField(x, y, MV_DOWN);
8327       started_moving = TRUE;
8328     }
8329     else if (element == EL_AMOEBA_DROP)
8330     {
8331       Feld[x][y] = EL_AMOEBA_GROWING;
8332       Store[x][y] = EL_AMOEBA_WET;
8333     }
8334     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8335               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8336              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8337              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8338     {
8339       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8340                                 (IS_FREE(x - 1, y + 1) ||
8341                                  Feld[x - 1][y + 1] == EL_ACID));
8342       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8343                                 (IS_FREE(x + 1, y + 1) ||
8344                                  Feld[x + 1][y + 1] == EL_ACID));
8345       boolean can_fall_any  = (can_fall_left || can_fall_right);
8346       boolean can_fall_both = (can_fall_left && can_fall_right);
8347       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8348
8349 #if USE_NEW_ALL_SLIPPERY
8350       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8351       {
8352         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8353           can_fall_right = FALSE;
8354         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8355           can_fall_left = FALSE;
8356         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8357           can_fall_right = FALSE;
8358         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8359           can_fall_left = FALSE;
8360
8361         can_fall_any  = (can_fall_left || can_fall_right);
8362         can_fall_both = FALSE;
8363       }
8364 #else
8365       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8366       {
8367         if (slippery_type == SLIPPERY_ONLY_LEFT)
8368           can_fall_right = FALSE;
8369         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8370           can_fall_left = FALSE;
8371         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8372           can_fall_right = FALSE;
8373         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8374           can_fall_left = FALSE;
8375
8376         can_fall_any  = (can_fall_left || can_fall_right);
8377         can_fall_both = (can_fall_left && can_fall_right);
8378       }
8379 #endif
8380
8381 #if USE_NEW_ALL_SLIPPERY
8382 #else
8383 #if USE_NEW_SP_SLIPPERY
8384       /* !!! better use the same properties as for custom elements here !!! */
8385       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8386                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8387       {
8388         can_fall_right = FALSE;         /* slip down on left side */
8389         can_fall_both = FALSE;
8390       }
8391 #endif
8392 #endif
8393
8394 #if USE_NEW_ALL_SLIPPERY
8395       if (can_fall_both)
8396       {
8397         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8398           can_fall_right = FALSE;       /* slip down on left side */
8399         else
8400           can_fall_left = !(can_fall_right = RND(2));
8401
8402         can_fall_both = FALSE;
8403       }
8404 #else
8405       if (can_fall_both)
8406       {
8407         if (game.emulation == EMU_BOULDERDASH ||
8408             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8409           can_fall_right = FALSE;       /* slip down on left side */
8410         else
8411           can_fall_left = !(can_fall_right = RND(2));
8412
8413         can_fall_both = FALSE;
8414       }
8415 #endif
8416
8417       if (can_fall_any)
8418       {
8419         /* if not determined otherwise, prefer left side for slipping down */
8420         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8421         started_moving = TRUE;
8422       }
8423     }
8424 #if 0
8425     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8426 #else
8427     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8428 #endif
8429     {
8430       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8431       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8432       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8433       int belt_dir = game.belt_dir[belt_nr];
8434
8435       if ((belt_dir == MV_LEFT  && left_is_free) ||
8436           (belt_dir == MV_RIGHT && right_is_free))
8437       {
8438         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8439
8440         InitMovingField(x, y, belt_dir);
8441         started_moving = TRUE;
8442
8443         Pushed[x][y] = TRUE;
8444         Pushed[nextx][y] = TRUE;
8445
8446         GfxAction[x][y] = ACTION_DEFAULT;
8447       }
8448       else
8449       {
8450         MovDir[x][y] = 0;       /* if element was moving, stop it */
8451       }
8452     }
8453   }
8454
8455   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8456 #if 0
8457   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8458 #else
8459   if (CAN_MOVE(element) && !started_moving)
8460 #endif
8461   {
8462     int move_pattern = element_info[element].move_pattern;
8463     int newx, newy;
8464
8465 #if 0
8466 #if DEBUG
8467     if (MovDir[x][y] == MV_NONE)
8468     {
8469       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8470              x, y, element, element_info[element].token_name);
8471       printf("StartMoving(): This should never happen!\n");
8472     }
8473 #endif
8474 #endif
8475
8476     Moving2Blocked(x, y, &newx, &newy);
8477
8478     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8479       return;
8480
8481     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8482         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8483     {
8484       WasJustMoving[x][y] = 0;
8485       CheckCollision[x][y] = 0;
8486
8487       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8488
8489       if (Feld[x][y] != element)        /* element has changed */
8490         return;
8491     }
8492
8493     if (!MovDelay[x][y])        /* start new movement phase */
8494     {
8495       /* all objects that can change their move direction after each step
8496          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8497
8498       if (element != EL_YAMYAM &&
8499           element != EL_DARK_YAMYAM &&
8500           element != EL_PACMAN &&
8501           !(move_pattern & MV_ANY_DIRECTION) &&
8502           move_pattern != MV_TURNING_LEFT &&
8503           move_pattern != MV_TURNING_RIGHT &&
8504           move_pattern != MV_TURNING_LEFT_RIGHT &&
8505           move_pattern != MV_TURNING_RIGHT_LEFT &&
8506           move_pattern != MV_TURNING_RANDOM)
8507       {
8508         TurnRound(x, y);
8509
8510         if (MovDelay[x][y] && (element == EL_BUG ||
8511                                element == EL_SPACESHIP ||
8512                                element == EL_SP_SNIKSNAK ||
8513                                element == EL_SP_ELECTRON ||
8514                                element == EL_MOLE))
8515           TEST_DrawLevelField(x, y);
8516       }
8517     }
8518
8519     if (MovDelay[x][y])         /* wait some time before next movement */
8520     {
8521       MovDelay[x][y]--;
8522
8523       if (element == EL_ROBOT ||
8524           element == EL_YAMYAM ||
8525           element == EL_DARK_YAMYAM)
8526       {
8527         DrawLevelElementAnimationIfNeeded(x, y, element);
8528         PlayLevelSoundAction(x, y, ACTION_WAITING);
8529       }
8530       else if (element == EL_SP_ELECTRON)
8531         DrawLevelElementAnimationIfNeeded(x, y, element);
8532       else if (element == EL_DRAGON)
8533       {
8534         int i;
8535         int dir = MovDir[x][y];
8536         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8537         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8538         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8539                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8540                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8541                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8542         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8543
8544         GfxAction[x][y] = ACTION_ATTACKING;
8545
8546         if (IS_PLAYER(x, y))
8547           DrawPlayerField(x, y);
8548         else
8549           TEST_DrawLevelField(x, y);
8550
8551         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8552
8553         for (i = 1; i <= 3; i++)
8554         {
8555           int xx = x + i * dx;
8556           int yy = y + i * dy;
8557           int sx = SCREENX(xx);
8558           int sy = SCREENY(yy);
8559           int flame_graphic = graphic + (i - 1);
8560
8561           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8562             break;
8563
8564           if (MovDelay[x][y])
8565           {
8566             int flamed = MovingOrBlocked2Element(xx, yy);
8567
8568             /* !!! */
8569 #if 0
8570             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8571               Bang(xx, yy);
8572             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8573               RemoveMovingField(xx, yy);
8574             else
8575               RemoveField(xx, yy);
8576 #else
8577             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8578               Bang(xx, yy);
8579             else
8580               RemoveMovingField(xx, yy);
8581 #endif
8582
8583             ChangeDelay[xx][yy] = 0;
8584
8585             Feld[xx][yy] = EL_FLAMES;
8586
8587             if (IN_SCR_FIELD(sx, sy))
8588             {
8589               TEST_DrawLevelFieldCrumbled(xx, yy);
8590               DrawGraphic(sx, sy, flame_graphic, frame);
8591             }
8592           }
8593           else
8594           {
8595             if (Feld[xx][yy] == EL_FLAMES)
8596               Feld[xx][yy] = EL_EMPTY;
8597             TEST_DrawLevelField(xx, yy);
8598           }
8599         }
8600       }
8601
8602       if (MovDelay[x][y])       /* element still has to wait some time */
8603       {
8604         PlayLevelSoundAction(x, y, ACTION_WAITING);
8605
8606         return;
8607       }
8608     }
8609
8610     /* now make next step */
8611
8612     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8613
8614     if (DONT_COLLIDE_WITH(element) &&
8615         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8616         !PLAYER_ENEMY_PROTECTED(newx, newy))
8617     {
8618       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8619
8620       return;
8621     }
8622
8623     else if (CAN_MOVE_INTO_ACID(element) &&
8624              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8625              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8626              (MovDir[x][y] == MV_DOWN ||
8627               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8628     {
8629       SplashAcid(newx, newy);
8630       Store[x][y] = EL_ACID;
8631     }
8632     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8633     {
8634       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8635           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8636           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8637           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8638       {
8639         RemoveField(x, y);
8640         TEST_DrawLevelField(x, y);
8641
8642         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8643         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8644           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8645
8646         local_player->friends_still_needed--;
8647         if (!local_player->friends_still_needed &&
8648             !local_player->GameOver && AllPlayersGone)
8649           PlayerWins(local_player);
8650
8651         return;
8652       }
8653       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8654       {
8655         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8656           TEST_DrawLevelField(newx, newy);
8657         else
8658           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8659       }
8660       else if (!IS_FREE(newx, newy))
8661       {
8662         GfxAction[x][y] = ACTION_WAITING;
8663
8664         if (IS_PLAYER(x, y))
8665           DrawPlayerField(x, y);
8666         else
8667           TEST_DrawLevelField(x, y);
8668
8669         return;
8670       }
8671     }
8672     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8673     {
8674       if (IS_FOOD_PIG(Feld[newx][newy]))
8675       {
8676         if (IS_MOVING(newx, newy))
8677           RemoveMovingField(newx, newy);
8678         else
8679         {
8680           Feld[newx][newy] = EL_EMPTY;
8681           TEST_DrawLevelField(newx, newy);
8682         }
8683
8684         PlayLevelSound(x, y, SND_PIG_DIGGING);
8685       }
8686       else if (!IS_FREE(newx, newy))
8687       {
8688         if (IS_PLAYER(x, y))
8689           DrawPlayerField(x, y);
8690         else
8691           TEST_DrawLevelField(x, y);
8692
8693         return;
8694       }
8695     }
8696     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8697     {
8698       if (Store[x][y] != EL_EMPTY)
8699       {
8700         boolean can_clone = FALSE;
8701         int xx, yy;
8702
8703         /* check if element to clone is still there */
8704         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8705         {
8706           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8707           {
8708             can_clone = TRUE;
8709
8710             break;
8711           }
8712         }
8713
8714         /* cannot clone or target field not free anymore -- do not clone */
8715         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8716           Store[x][y] = EL_EMPTY;
8717       }
8718
8719       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8720       {
8721         if (IS_MV_DIAGONAL(MovDir[x][y]))
8722         {
8723           int diagonal_move_dir = MovDir[x][y];
8724           int stored = Store[x][y];
8725           int change_delay = 8;
8726           int graphic;
8727
8728           /* android is moving diagonally */
8729
8730           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8731
8732           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8733           GfxElement[x][y] = EL_EMC_ANDROID;
8734           GfxAction[x][y] = ACTION_SHRINKING;
8735           GfxDir[x][y] = diagonal_move_dir;
8736           ChangeDelay[x][y] = change_delay;
8737
8738           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8739                                    GfxDir[x][y]);
8740
8741           DrawLevelGraphicAnimation(x, y, graphic);
8742           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8743
8744           if (Feld[newx][newy] == EL_ACID)
8745           {
8746             SplashAcid(newx, newy);
8747
8748             return;
8749           }
8750
8751           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8752
8753           Store[newx][newy] = EL_EMC_ANDROID;
8754           GfxElement[newx][newy] = EL_EMC_ANDROID;
8755           GfxAction[newx][newy] = ACTION_GROWING;
8756           GfxDir[newx][newy] = diagonal_move_dir;
8757           ChangeDelay[newx][newy] = change_delay;
8758
8759           graphic = el_act_dir2img(GfxElement[newx][newy],
8760                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8761
8762           DrawLevelGraphicAnimation(newx, newy, graphic);
8763           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8764
8765           return;
8766         }
8767         else
8768         {
8769           Feld[newx][newy] = EL_EMPTY;
8770           TEST_DrawLevelField(newx, newy);
8771
8772           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8773         }
8774       }
8775       else if (!IS_FREE(newx, newy))
8776       {
8777 #if 0
8778         if (IS_PLAYER(x, y))
8779           DrawPlayerField(x, y);
8780         else
8781           TEST_DrawLevelField(x, y);
8782 #endif
8783
8784         return;
8785       }
8786     }
8787     else if (IS_CUSTOM_ELEMENT(element) &&
8788              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8789     {
8790 #if 1
8791       if (!DigFieldByCE(newx, newy, element))
8792         return;
8793 #else
8794       int new_element = Feld[newx][newy];
8795
8796       if (!IS_FREE(newx, newy))
8797       {
8798         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8799                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8800                       ACTION_BREAKING);
8801
8802         /* no element can dig solid indestructible elements */
8803         if (IS_INDESTRUCTIBLE(new_element) &&
8804             !IS_DIGGABLE(new_element) &&
8805             !IS_COLLECTIBLE(new_element))
8806           return;
8807
8808         if (AmoebaNr[newx][newy] &&
8809             (new_element == EL_AMOEBA_FULL ||
8810              new_element == EL_BD_AMOEBA ||
8811              new_element == EL_AMOEBA_GROWING))
8812         {
8813           AmoebaCnt[AmoebaNr[newx][newy]]--;
8814           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8815         }
8816
8817         if (IS_MOVING(newx, newy))
8818           RemoveMovingField(newx, newy);
8819         else
8820         {
8821           RemoveField(newx, newy);
8822           TEST_DrawLevelField(newx, newy);
8823         }
8824
8825         /* if digged element was about to explode, prevent the explosion */
8826         ExplodeField[newx][newy] = EX_TYPE_NONE;
8827
8828         PlayLevelSoundAction(x, y, action);
8829       }
8830
8831       Store[newx][newy] = EL_EMPTY;
8832
8833 #if 1
8834       /* this makes it possible to leave the removed element again */
8835       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8836         Store[newx][newy] = new_element;
8837 #else
8838       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8839       {
8840         int move_leave_element = element_info[element].move_leave_element;
8841
8842         /* this makes it possible to leave the removed element again */
8843         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8844                              new_element : move_leave_element);
8845       }
8846 #endif
8847
8848 #endif
8849
8850       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8851       {
8852         RunnerVisit[x][y] = FrameCounter;
8853         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8854       }
8855     }
8856     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8857     {
8858       if (!IS_FREE(newx, newy))
8859       {
8860         if (IS_PLAYER(x, y))
8861           DrawPlayerField(x, y);
8862         else
8863           TEST_DrawLevelField(x, y);
8864
8865         return;
8866       }
8867       else
8868       {
8869         boolean wanna_flame = !RND(10);
8870         int dx = newx - x, dy = newy - y;
8871         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8872         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8873         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8874                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8875         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8876                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8877
8878         if ((wanna_flame ||
8879              IS_CLASSIC_ENEMY(element1) ||
8880              IS_CLASSIC_ENEMY(element2)) &&
8881             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8882             element1 != EL_FLAMES && element2 != EL_FLAMES)
8883         {
8884           ResetGfxAnimation(x, y);
8885           GfxAction[x][y] = ACTION_ATTACKING;
8886
8887           if (IS_PLAYER(x, y))
8888             DrawPlayerField(x, y);
8889           else
8890             TEST_DrawLevelField(x, y);
8891
8892           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8893
8894           MovDelay[x][y] = 50;
8895
8896           /* !!! */
8897 #if 0
8898           RemoveField(newx, newy);
8899 #endif
8900           Feld[newx][newy] = EL_FLAMES;
8901           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8902           {
8903 #if 0
8904             RemoveField(newx1, newy1);
8905 #endif
8906             Feld[newx1][newy1] = EL_FLAMES;
8907           }
8908           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8909           {
8910 #if 0
8911             RemoveField(newx2, newy2);
8912 #endif
8913             Feld[newx2][newy2] = EL_FLAMES;
8914           }
8915
8916           return;
8917         }
8918       }
8919     }
8920     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8921              Feld[newx][newy] == EL_DIAMOND)
8922     {
8923       if (IS_MOVING(newx, newy))
8924         RemoveMovingField(newx, newy);
8925       else
8926       {
8927         Feld[newx][newy] = EL_EMPTY;
8928         TEST_DrawLevelField(newx, newy);
8929       }
8930
8931       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8932     }
8933     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8934              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8935     {
8936       if (AmoebaNr[newx][newy])
8937       {
8938         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8939         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8940             Feld[newx][newy] == EL_BD_AMOEBA)
8941           AmoebaCnt[AmoebaNr[newx][newy]]--;
8942       }
8943
8944 #if 0
8945       /* !!! test !!! */
8946       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8947       {
8948         RemoveMovingField(newx, newy);
8949       }
8950 #else
8951       if (IS_MOVING(newx, newy))
8952       {
8953         RemoveMovingField(newx, newy);
8954       }
8955 #endif
8956       else
8957       {
8958         Feld[newx][newy] = EL_EMPTY;
8959         TEST_DrawLevelField(newx, newy);
8960       }
8961
8962       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8963     }
8964     else if ((element == EL_PACMAN || element == EL_MOLE)
8965              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8966     {
8967       if (AmoebaNr[newx][newy])
8968       {
8969         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8970         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8971             Feld[newx][newy] == EL_BD_AMOEBA)
8972           AmoebaCnt[AmoebaNr[newx][newy]]--;
8973       }
8974
8975       if (element == EL_MOLE)
8976       {
8977         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8978         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8979
8980         ResetGfxAnimation(x, y);
8981         GfxAction[x][y] = ACTION_DIGGING;
8982         TEST_DrawLevelField(x, y);
8983
8984         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8985
8986         return;                         /* wait for shrinking amoeba */
8987       }
8988       else      /* element == EL_PACMAN */
8989       {
8990         Feld[newx][newy] = EL_EMPTY;
8991         TEST_DrawLevelField(newx, newy);
8992         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8993       }
8994     }
8995     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8996              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8997               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8998     {
8999       /* wait for shrinking amoeba to completely disappear */
9000       return;
9001     }
9002     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9003     {
9004       /* object was running against a wall */
9005
9006       TurnRound(x, y);
9007
9008 #if 0
9009       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9010       if (move_pattern & MV_ANY_DIRECTION &&
9011           move_pattern == MovDir[x][y])
9012       {
9013         int blocking_element =
9014           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9015
9016         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9017                                  MovDir[x][y]);
9018
9019         element = Feld[x][y];   /* element might have changed */
9020       }
9021 #endif
9022
9023       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9024         DrawLevelElementAnimation(x, y, element);
9025
9026       if (DONT_TOUCH(element))
9027         TestIfBadThingTouchesPlayer(x, y);
9028
9029       return;
9030     }
9031
9032     InitMovingField(x, y, MovDir[x][y]);
9033
9034     PlayLevelSoundAction(x, y, ACTION_MOVING);
9035   }
9036
9037   if (MovDir[x][y])
9038     ContinueMoving(x, y);
9039 }
9040
9041 void ContinueMoving(int x, int y)
9042 {
9043   int element = Feld[x][y];
9044   struct ElementInfo *ei = &element_info[element];
9045   int direction = MovDir[x][y];
9046   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9047   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9048   int newx = x + dx, newy = y + dy;
9049   int stored = Store[x][y];
9050   int stored_new = Store[newx][newy];
9051   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9052   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9053   boolean last_line = (newy == lev_fieldy - 1);
9054
9055   MovPos[x][y] += getElementMoveStepsize(x, y);
9056
9057   if (pushed_by_player) /* special case: moving object pushed by player */
9058     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9059
9060   if (ABS(MovPos[x][y]) < TILEX)
9061   {
9062 #if 0
9063     int ee = Feld[x][y];
9064     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9065     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9066
9067     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9068            x, y, ABS(MovPos[x][y]),
9069            ee, gg, ff,
9070            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9071 #endif
9072
9073     TEST_DrawLevelField(x, y);
9074
9075     return;     /* element is still moving */
9076   }
9077
9078   /* element reached destination field */
9079
9080   Feld[x][y] = EL_EMPTY;
9081   Feld[newx][newy] = element;
9082   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9083
9084   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9085   {
9086     element = Feld[newx][newy] = EL_ACID;
9087   }
9088   else if (element == EL_MOLE)
9089   {
9090     Feld[x][y] = EL_SAND;
9091
9092     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9093   }
9094   else if (element == EL_QUICKSAND_FILLING)
9095   {
9096     element = Feld[newx][newy] = get_next_element(element);
9097     Store[newx][newy] = Store[x][y];
9098   }
9099   else if (element == EL_QUICKSAND_EMPTYING)
9100   {
9101     Feld[x][y] = get_next_element(element);
9102     element = Feld[newx][newy] = Store[x][y];
9103   }
9104   else if (element == EL_QUICKSAND_FAST_FILLING)
9105   {
9106     element = Feld[newx][newy] = get_next_element(element);
9107     Store[newx][newy] = Store[x][y];
9108   }
9109   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9110   {
9111     Feld[x][y] = get_next_element(element);
9112     element = Feld[newx][newy] = Store[x][y];
9113   }
9114   else if (element == EL_MAGIC_WALL_FILLING)
9115   {
9116     element = Feld[newx][newy] = get_next_element(element);
9117     if (!game.magic_wall_active)
9118       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9119     Store[newx][newy] = Store[x][y];
9120   }
9121   else if (element == EL_MAGIC_WALL_EMPTYING)
9122   {
9123     Feld[x][y] = get_next_element(element);
9124     if (!game.magic_wall_active)
9125       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9126     element = Feld[newx][newy] = Store[x][y];
9127
9128 #if USE_NEW_CUSTOM_VALUE
9129     InitField(newx, newy, FALSE);
9130 #endif
9131   }
9132   else if (element == EL_BD_MAGIC_WALL_FILLING)
9133   {
9134     element = Feld[newx][newy] = get_next_element(element);
9135     if (!game.magic_wall_active)
9136       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9137     Store[newx][newy] = Store[x][y];
9138   }
9139   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9140   {
9141     Feld[x][y] = get_next_element(element);
9142     if (!game.magic_wall_active)
9143       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9144     element = Feld[newx][newy] = Store[x][y];
9145
9146 #if USE_NEW_CUSTOM_VALUE
9147     InitField(newx, newy, FALSE);
9148 #endif
9149   }
9150   else if (element == EL_DC_MAGIC_WALL_FILLING)
9151   {
9152     element = Feld[newx][newy] = get_next_element(element);
9153     if (!game.magic_wall_active)
9154       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9155     Store[newx][newy] = Store[x][y];
9156   }
9157   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9158   {
9159     Feld[x][y] = get_next_element(element);
9160     if (!game.magic_wall_active)
9161       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9162     element = Feld[newx][newy] = Store[x][y];
9163
9164 #if USE_NEW_CUSTOM_VALUE
9165     InitField(newx, newy, FALSE);
9166 #endif
9167   }
9168   else if (element == EL_AMOEBA_DROPPING)
9169   {
9170     Feld[x][y] = get_next_element(element);
9171     element = Feld[newx][newy] = Store[x][y];
9172   }
9173   else if (element == EL_SOKOBAN_OBJECT)
9174   {
9175     if (Back[x][y])
9176       Feld[x][y] = Back[x][y];
9177
9178     if (Back[newx][newy])
9179       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9180
9181     Back[x][y] = Back[newx][newy] = 0;
9182   }
9183
9184   Store[x][y] = EL_EMPTY;
9185   MovPos[x][y] = 0;
9186   MovDir[x][y] = 0;
9187   MovDelay[x][y] = 0;
9188
9189   MovDelay[newx][newy] = 0;
9190
9191   if (CAN_CHANGE_OR_HAS_ACTION(element))
9192   {
9193     /* copy element change control values to new field */
9194     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9195     ChangePage[newx][newy]  = ChangePage[x][y];
9196     ChangeCount[newx][newy] = ChangeCount[x][y];
9197     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9198   }
9199
9200 #if USE_NEW_CUSTOM_VALUE
9201   CustomValue[newx][newy] = CustomValue[x][y];
9202 #endif
9203
9204   ChangeDelay[x][y] = 0;
9205   ChangePage[x][y] = -1;
9206   ChangeCount[x][y] = 0;
9207   ChangeEvent[x][y] = -1;
9208
9209 #if USE_NEW_CUSTOM_VALUE
9210   CustomValue[x][y] = 0;
9211 #endif
9212
9213   /* copy animation control values to new field */
9214   GfxFrame[newx][newy]  = GfxFrame[x][y];
9215   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9216   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9217   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9218
9219   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9220
9221   /* some elements can leave other elements behind after moving */
9222 #if 1
9223   if (ei->move_leave_element != EL_EMPTY &&
9224       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9225       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9226 #else
9227   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9228       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9229       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9230 #endif
9231   {
9232     int move_leave_element = ei->move_leave_element;
9233
9234 #if 1
9235 #if 1
9236     /* this makes it possible to leave the removed element again */
9237     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9238       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9239 #else
9240     /* this makes it possible to leave the removed element again */
9241     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9242       move_leave_element = stored;
9243 #endif
9244 #else
9245     /* this makes it possible to leave the removed element again */
9246     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9247         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9248       move_leave_element = stored;
9249 #endif
9250
9251     Feld[x][y] = move_leave_element;
9252
9253     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9254       MovDir[x][y] = direction;
9255
9256     InitField(x, y, FALSE);
9257
9258     if (GFX_CRUMBLED(Feld[x][y]))
9259       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9260
9261     if (ELEM_IS_PLAYER(move_leave_element))
9262       RelocatePlayer(x, y, move_leave_element);
9263   }
9264
9265   /* do this after checking for left-behind element */
9266   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9267
9268   if (!CAN_MOVE(element) ||
9269       (CAN_FALL(element) && direction == MV_DOWN &&
9270        (element == EL_SPRING ||
9271         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9272         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9273     GfxDir[x][y] = MovDir[newx][newy] = 0;
9274
9275   TEST_DrawLevelField(x, y);
9276   TEST_DrawLevelField(newx, newy);
9277
9278   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9279
9280   /* prevent pushed element from moving on in pushed direction */
9281   if (pushed_by_player && CAN_MOVE(element) &&
9282       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9283       !(element_info[element].move_pattern & direction))
9284     TurnRound(newx, newy);
9285
9286   /* prevent elements on conveyor belt from moving on in last direction */
9287   if (pushed_by_conveyor && CAN_FALL(element) &&
9288       direction & MV_HORIZONTAL)
9289     MovDir[newx][newy] = 0;
9290
9291   if (!pushed_by_player)
9292   {
9293     int nextx = newx + dx, nexty = newy + dy;
9294     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9295
9296     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9297
9298     if (CAN_FALL(element) && direction == MV_DOWN)
9299       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9300
9301     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9302       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9303
9304 #if USE_FIX_IMPACT_COLLISION
9305     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9306       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9307 #endif
9308   }
9309
9310   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9311   {
9312     TestIfBadThingTouchesPlayer(newx, newy);
9313     TestIfBadThingTouchesFriend(newx, newy);
9314
9315     if (!IS_CUSTOM_ELEMENT(element))
9316       TestIfBadThingTouchesOtherBadThing(newx, newy);
9317   }
9318   else if (element == EL_PENGUIN)
9319     TestIfFriendTouchesBadThing(newx, newy);
9320
9321   if (DONT_GET_HIT_BY(element))
9322   {
9323     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9324   }
9325
9326   /* give the player one last chance (one more frame) to move away */
9327   if (CAN_FALL(element) && direction == MV_DOWN &&
9328       (last_line || (!IS_FREE(x, newy + 1) &&
9329                      (!IS_PLAYER(x, newy + 1) ||
9330                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9331     Impact(x, newy);
9332
9333   if (pushed_by_player && !game.use_change_when_pushing_bug)
9334   {
9335     int push_side = MV_DIR_OPPOSITE(direction);
9336     struct PlayerInfo *player = PLAYERINFO(x, y);
9337
9338     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9339                                player->index_bit, push_side);
9340     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9341                                         player->index_bit, push_side);
9342   }
9343
9344   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9345     MovDelay[newx][newy] = 1;
9346
9347   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9348
9349   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9350
9351 #if 0
9352   if (ChangePage[newx][newy] != -1)             /* delayed change */
9353   {
9354     int page = ChangePage[newx][newy];
9355     struct ElementChangeInfo *change = &ei->change_page[page];
9356
9357     ChangePage[newx][newy] = -1;
9358
9359     if (change->can_change)
9360     {
9361       if (ChangeElement(newx, newy, element, page))
9362       {
9363         if (change->post_change_function)
9364           change->post_change_function(newx, newy);
9365       }
9366     }
9367
9368     if (change->has_action)
9369       ExecuteCustomElementAction(newx, newy, element, page);
9370   }
9371 #endif
9372
9373   TestIfElementHitsCustomElement(newx, newy, direction);
9374   TestIfPlayerTouchesCustomElement(newx, newy);
9375   TestIfElementTouchesCustomElement(newx, newy);
9376
9377   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9378       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9379     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9380                              MV_DIR_OPPOSITE(direction));
9381 }
9382
9383 int AmoebeNachbarNr(int ax, int ay)
9384 {
9385   int i;
9386   int element = Feld[ax][ay];
9387   int group_nr = 0;
9388   static int xy[4][2] =
9389   {
9390     { 0, -1 },
9391     { -1, 0 },
9392     { +1, 0 },
9393     { 0, +1 }
9394   };
9395
9396   for (i = 0; i < NUM_DIRECTIONS; i++)
9397   {
9398     int x = ax + xy[i][0];
9399     int y = ay + xy[i][1];
9400
9401     if (!IN_LEV_FIELD(x, y))
9402       continue;
9403
9404     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9405       group_nr = AmoebaNr[x][y];
9406   }
9407
9408   return group_nr;
9409 }
9410
9411 void AmoebenVereinigen(int ax, int ay)
9412 {
9413   int i, x, y, xx, yy;
9414   int new_group_nr = AmoebaNr[ax][ay];
9415   static int xy[4][2] =
9416   {
9417     { 0, -1 },
9418     { -1, 0 },
9419     { +1, 0 },
9420     { 0, +1 }
9421   };
9422
9423   if (new_group_nr == 0)
9424     return;
9425
9426   for (i = 0; i < NUM_DIRECTIONS; i++)
9427   {
9428     x = ax + xy[i][0];
9429     y = ay + xy[i][1];
9430
9431     if (!IN_LEV_FIELD(x, y))
9432       continue;
9433
9434     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9435          Feld[x][y] == EL_BD_AMOEBA ||
9436          Feld[x][y] == EL_AMOEBA_DEAD) &&
9437         AmoebaNr[x][y] != new_group_nr)
9438     {
9439       int old_group_nr = AmoebaNr[x][y];
9440
9441       if (old_group_nr == 0)
9442         return;
9443
9444       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9445       AmoebaCnt[old_group_nr] = 0;
9446       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9447       AmoebaCnt2[old_group_nr] = 0;
9448
9449       SCAN_PLAYFIELD(xx, yy)
9450       {
9451         if (AmoebaNr[xx][yy] == old_group_nr)
9452           AmoebaNr[xx][yy] = new_group_nr;
9453       }
9454     }
9455   }
9456 }
9457
9458 void AmoebeUmwandeln(int ax, int ay)
9459 {
9460   int i, x, y;
9461
9462   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9463   {
9464     int group_nr = AmoebaNr[ax][ay];
9465
9466 #ifdef DEBUG
9467     if (group_nr == 0)
9468     {
9469       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9470       printf("AmoebeUmwandeln(): This should never happen!\n");
9471       return;
9472     }
9473 #endif
9474
9475     SCAN_PLAYFIELD(x, y)
9476     {
9477       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9478       {
9479         AmoebaNr[x][y] = 0;
9480         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9481       }
9482     }
9483
9484     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9485                             SND_AMOEBA_TURNING_TO_GEM :
9486                             SND_AMOEBA_TURNING_TO_ROCK));
9487     Bang(ax, ay);
9488   }
9489   else
9490   {
9491     static int xy[4][2] =
9492     {
9493       { 0, -1 },
9494       { -1, 0 },
9495       { +1, 0 },
9496       { 0, +1 }
9497     };
9498
9499     for (i = 0; i < NUM_DIRECTIONS; i++)
9500     {
9501       x = ax + xy[i][0];
9502       y = ay + xy[i][1];
9503
9504       if (!IN_LEV_FIELD(x, y))
9505         continue;
9506
9507       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9508       {
9509         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9510                               SND_AMOEBA_TURNING_TO_GEM :
9511                               SND_AMOEBA_TURNING_TO_ROCK));
9512         Bang(x, y);
9513       }
9514     }
9515   }
9516 }
9517
9518 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9519 {
9520   int x, y;
9521   int group_nr = AmoebaNr[ax][ay];
9522   boolean done = FALSE;
9523
9524 #ifdef DEBUG
9525   if (group_nr == 0)
9526   {
9527     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9528     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9529     return;
9530   }
9531 #endif
9532
9533   SCAN_PLAYFIELD(x, y)
9534   {
9535     if (AmoebaNr[x][y] == group_nr &&
9536         (Feld[x][y] == EL_AMOEBA_DEAD ||
9537          Feld[x][y] == EL_BD_AMOEBA ||
9538          Feld[x][y] == EL_AMOEBA_GROWING))
9539     {
9540       AmoebaNr[x][y] = 0;
9541       Feld[x][y] = new_element;
9542       InitField(x, y, FALSE);
9543       TEST_DrawLevelField(x, y);
9544       done = TRUE;
9545     }
9546   }
9547
9548   if (done)
9549     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9550                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9551                             SND_BD_AMOEBA_TURNING_TO_GEM));
9552 }
9553
9554 void AmoebeWaechst(int x, int y)
9555 {
9556   static unsigned long sound_delay = 0;
9557   static unsigned long sound_delay_value = 0;
9558
9559   if (!MovDelay[x][y])          /* start new growing cycle */
9560   {
9561     MovDelay[x][y] = 7;
9562
9563     if (DelayReached(&sound_delay, sound_delay_value))
9564     {
9565       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9566       sound_delay_value = 30;
9567     }
9568   }
9569
9570   if (MovDelay[x][y])           /* wait some time before growing bigger */
9571   {
9572     MovDelay[x][y]--;
9573     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9574     {
9575       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9576                                            6 - MovDelay[x][y]);
9577
9578       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9579     }
9580
9581     if (!MovDelay[x][y])
9582     {
9583       Feld[x][y] = Store[x][y];
9584       Store[x][y] = 0;
9585       TEST_DrawLevelField(x, y);
9586     }
9587   }
9588 }
9589
9590 void AmoebaDisappearing(int x, int y)
9591 {
9592   static unsigned long sound_delay = 0;
9593   static unsigned long sound_delay_value = 0;
9594
9595   if (!MovDelay[x][y])          /* start new shrinking cycle */
9596   {
9597     MovDelay[x][y] = 7;
9598
9599     if (DelayReached(&sound_delay, sound_delay_value))
9600       sound_delay_value = 30;
9601   }
9602
9603   if (MovDelay[x][y])           /* wait some time before shrinking */
9604   {
9605     MovDelay[x][y]--;
9606     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9607     {
9608       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9609                                            6 - MovDelay[x][y]);
9610
9611       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9612     }
9613
9614     if (!MovDelay[x][y])
9615     {
9616       Feld[x][y] = EL_EMPTY;
9617       TEST_DrawLevelField(x, y);
9618
9619       /* don't let mole enter this field in this cycle;
9620          (give priority to objects falling to this field from above) */
9621       Stop[x][y] = TRUE;
9622     }
9623   }
9624 }
9625
9626 void AmoebeAbleger(int ax, int ay)
9627 {
9628   int i;
9629   int element = Feld[ax][ay];
9630   int graphic = el2img(element);
9631   int newax = ax, neway = ay;
9632   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9633   static int xy[4][2] =
9634   {
9635     { 0, -1 },
9636     { -1, 0 },
9637     { +1, 0 },
9638     { 0, +1 }
9639   };
9640
9641   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9642   {
9643     Feld[ax][ay] = EL_AMOEBA_DEAD;
9644     TEST_DrawLevelField(ax, ay);
9645     return;
9646   }
9647
9648   if (IS_ANIMATED(graphic))
9649     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9650
9651   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9652     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9653
9654   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9655   {
9656     MovDelay[ax][ay]--;
9657     if (MovDelay[ax][ay])
9658       return;
9659   }
9660
9661   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9662   {
9663     int start = RND(4);
9664     int x = ax + xy[start][0];
9665     int y = ay + xy[start][1];
9666
9667     if (!IN_LEV_FIELD(x, y))
9668       return;
9669
9670     if (IS_FREE(x, y) ||
9671         CAN_GROW_INTO(Feld[x][y]) ||
9672         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9673         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9674     {
9675       newax = x;
9676       neway = y;
9677     }
9678
9679     if (newax == ax && neway == ay)
9680       return;
9681   }
9682   else                          /* normal or "filled" (BD style) amoeba */
9683   {
9684     int start = RND(4);
9685     boolean waiting_for_player = FALSE;
9686
9687     for (i = 0; i < NUM_DIRECTIONS; i++)
9688     {
9689       int j = (start + i) % 4;
9690       int x = ax + xy[j][0];
9691       int y = ay + xy[j][1];
9692
9693       if (!IN_LEV_FIELD(x, y))
9694         continue;
9695
9696       if (IS_FREE(x, y) ||
9697           CAN_GROW_INTO(Feld[x][y]) ||
9698           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9699           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9700       {
9701         newax = x;
9702         neway = y;
9703         break;
9704       }
9705       else if (IS_PLAYER(x, y))
9706         waiting_for_player = TRUE;
9707     }
9708
9709     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9710     {
9711       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9712       {
9713         Feld[ax][ay] = EL_AMOEBA_DEAD;
9714         TEST_DrawLevelField(ax, ay);
9715         AmoebaCnt[AmoebaNr[ax][ay]]--;
9716
9717         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9718         {
9719           if (element == EL_AMOEBA_FULL)
9720             AmoebeUmwandeln(ax, ay);
9721           else if (element == EL_BD_AMOEBA)
9722             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9723         }
9724       }
9725       return;
9726     }
9727     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9728     {
9729       /* amoeba gets larger by growing in some direction */
9730
9731       int new_group_nr = AmoebaNr[ax][ay];
9732
9733 #ifdef DEBUG
9734   if (new_group_nr == 0)
9735   {
9736     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9737     printf("AmoebeAbleger(): This should never happen!\n");
9738     return;
9739   }
9740 #endif
9741
9742       AmoebaNr[newax][neway] = new_group_nr;
9743       AmoebaCnt[new_group_nr]++;
9744       AmoebaCnt2[new_group_nr]++;
9745
9746       /* if amoeba touches other amoeba(s) after growing, unify them */
9747       AmoebenVereinigen(newax, neway);
9748
9749       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9750       {
9751         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9752         return;
9753       }
9754     }
9755   }
9756
9757   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9758       (neway == lev_fieldy - 1 && newax != ax))
9759   {
9760     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9761     Store[newax][neway] = element;
9762   }
9763   else if (neway == ay || element == EL_EMC_DRIPPER)
9764   {
9765     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9766
9767     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9768   }
9769   else
9770   {
9771     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9772     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9773     Store[ax][ay] = EL_AMOEBA_DROP;
9774     ContinueMoving(ax, ay);
9775     return;
9776   }
9777
9778   TEST_DrawLevelField(newax, neway);
9779 }
9780
9781 void Life(int ax, int ay)
9782 {
9783   int x1, y1, x2, y2;
9784   int life_time = 40;
9785   int element = Feld[ax][ay];
9786   int graphic = el2img(element);
9787   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9788                          level.biomaze);
9789   boolean changed = FALSE;
9790
9791   if (IS_ANIMATED(graphic))
9792     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9793
9794   if (Stop[ax][ay])
9795     return;
9796
9797   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9798     MovDelay[ax][ay] = life_time;
9799
9800   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9801   {
9802     MovDelay[ax][ay]--;
9803     if (MovDelay[ax][ay])
9804       return;
9805   }
9806
9807   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9808   {
9809     int xx = ax+x1, yy = ay+y1;
9810     int nachbarn = 0;
9811
9812     if (!IN_LEV_FIELD(xx, yy))
9813       continue;
9814
9815     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9816     {
9817       int x = xx+x2, y = yy+y2;
9818
9819       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9820         continue;
9821
9822       if (((Feld[x][y] == element ||
9823             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9824            !Stop[x][y]) ||
9825           (IS_FREE(x, y) && Stop[x][y]))
9826         nachbarn++;
9827     }
9828
9829     if (xx == ax && yy == ay)           /* field in the middle */
9830     {
9831       if (nachbarn < life_parameter[0] ||
9832           nachbarn > life_parameter[1])
9833       {
9834         Feld[xx][yy] = EL_EMPTY;
9835         if (!Stop[xx][yy])
9836           TEST_DrawLevelField(xx, yy);
9837         Stop[xx][yy] = TRUE;
9838         changed = TRUE;
9839       }
9840     }
9841     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9842     {                                   /* free border field */
9843       if (nachbarn >= life_parameter[2] &&
9844           nachbarn <= life_parameter[3])
9845       {
9846         Feld[xx][yy] = element;
9847         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9848         if (!Stop[xx][yy])
9849           TEST_DrawLevelField(xx, yy);
9850         Stop[xx][yy] = TRUE;
9851         changed = TRUE;
9852       }
9853     }
9854   }
9855
9856   if (changed)
9857     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9858                    SND_GAME_OF_LIFE_GROWING);
9859 }
9860
9861 static void InitRobotWheel(int x, int y)
9862 {
9863   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9864 }
9865
9866 static void RunRobotWheel(int x, int y)
9867 {
9868   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9869 }
9870
9871 static void StopRobotWheel(int x, int y)
9872 {
9873   if (ZX == x && ZY == y)
9874   {
9875     ZX = ZY = -1;
9876
9877     game.robot_wheel_active = FALSE;
9878   }
9879 }
9880
9881 static void InitTimegateWheel(int x, int y)
9882 {
9883   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9884 }
9885
9886 static void RunTimegateWheel(int x, int y)
9887 {
9888   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9889 }
9890
9891 static void InitMagicBallDelay(int x, int y)
9892 {
9893 #if 1
9894   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9895 #else
9896   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9897 #endif
9898 }
9899
9900 static void ActivateMagicBall(int bx, int by)
9901 {
9902   int x, y;
9903
9904   if (level.ball_random)
9905   {
9906     int pos_border = RND(8);    /* select one of the eight border elements */
9907     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9908     int xx = pos_content % 3;
9909     int yy = pos_content / 3;
9910
9911     x = bx - 1 + xx;
9912     y = by - 1 + yy;
9913
9914     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9915       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9916   }
9917   else
9918   {
9919     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9920     {
9921       int xx = x - bx + 1;
9922       int yy = y - by + 1;
9923
9924       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9925         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9926     }
9927   }
9928
9929   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9930 }
9931
9932 void CheckExit(int x, int y)
9933 {
9934   if (local_player->gems_still_needed > 0 ||
9935       local_player->sokobanfields_still_needed > 0 ||
9936       local_player->lights_still_needed > 0)
9937   {
9938     int element = Feld[x][y];
9939     int graphic = el2img(element);
9940
9941     if (IS_ANIMATED(graphic))
9942       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9943
9944     return;
9945   }
9946
9947   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9948     return;
9949
9950   Feld[x][y] = EL_EXIT_OPENING;
9951
9952   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9953 }
9954
9955 void CheckExitEM(int x, int y)
9956 {
9957   if (local_player->gems_still_needed > 0 ||
9958       local_player->sokobanfields_still_needed > 0 ||
9959       local_player->lights_still_needed > 0)
9960   {
9961     int element = Feld[x][y];
9962     int graphic = el2img(element);
9963
9964     if (IS_ANIMATED(graphic))
9965       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9966
9967     return;
9968   }
9969
9970   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9971     return;
9972
9973   Feld[x][y] = EL_EM_EXIT_OPENING;
9974
9975   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9976 }
9977
9978 void CheckExitSteel(int x, int y)
9979 {
9980   if (local_player->gems_still_needed > 0 ||
9981       local_player->sokobanfields_still_needed > 0 ||
9982       local_player->lights_still_needed > 0)
9983   {
9984     int element = Feld[x][y];
9985     int graphic = el2img(element);
9986
9987     if (IS_ANIMATED(graphic))
9988       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9989
9990     return;
9991   }
9992
9993   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9994     return;
9995
9996   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9997
9998   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9999 }
10000
10001 void CheckExitSteelEM(int x, int y)
10002 {
10003   if (local_player->gems_still_needed > 0 ||
10004       local_player->sokobanfields_still_needed > 0 ||
10005       local_player->lights_still_needed > 0)
10006   {
10007     int element = Feld[x][y];
10008     int graphic = el2img(element);
10009
10010     if (IS_ANIMATED(graphic))
10011       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10012
10013     return;
10014   }
10015
10016   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10017     return;
10018
10019   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10020
10021   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10022 }
10023
10024 void CheckExitSP(int x, int y)
10025 {
10026   if (local_player->gems_still_needed > 0)
10027   {
10028     int element = Feld[x][y];
10029     int graphic = el2img(element);
10030
10031     if (IS_ANIMATED(graphic))
10032       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10033
10034     return;
10035   }
10036
10037   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10038     return;
10039
10040   Feld[x][y] = EL_SP_EXIT_OPENING;
10041
10042   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10043 }
10044
10045 static void CloseAllOpenTimegates()
10046 {
10047   int x, y;
10048
10049   SCAN_PLAYFIELD(x, y)
10050   {
10051     int element = Feld[x][y];
10052
10053     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10054     {
10055       Feld[x][y] = EL_TIMEGATE_CLOSING;
10056
10057       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10058     }
10059   }
10060 }
10061
10062 void DrawTwinkleOnField(int x, int y)
10063 {
10064   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10065     return;
10066
10067   if (Feld[x][y] == EL_BD_DIAMOND)
10068     return;
10069
10070   if (MovDelay[x][y] == 0)      /* next animation frame */
10071     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10072
10073   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10074   {
10075     MovDelay[x][y]--;
10076
10077     DrawLevelElementAnimation(x, y, Feld[x][y]);
10078
10079     if (MovDelay[x][y] != 0)
10080     {
10081       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10082                                            10 - MovDelay[x][y]);
10083
10084       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10085     }
10086   }
10087 }
10088
10089 void MauerWaechst(int x, int y)
10090 {
10091   int delay = 6;
10092
10093   if (!MovDelay[x][y])          /* next animation frame */
10094     MovDelay[x][y] = 3 * delay;
10095
10096   if (MovDelay[x][y])           /* wait some time before next frame */
10097   {
10098     MovDelay[x][y]--;
10099
10100     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10101     {
10102       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10103       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10104
10105       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10106     }
10107
10108     if (!MovDelay[x][y])
10109     {
10110       if (MovDir[x][y] == MV_LEFT)
10111       {
10112         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10113           TEST_DrawLevelField(x - 1, y);
10114       }
10115       else if (MovDir[x][y] == MV_RIGHT)
10116       {
10117         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10118           TEST_DrawLevelField(x + 1, y);
10119       }
10120       else if (MovDir[x][y] == MV_UP)
10121       {
10122         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10123           TEST_DrawLevelField(x, y - 1);
10124       }
10125       else
10126       {
10127         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10128           TEST_DrawLevelField(x, y + 1);
10129       }
10130
10131       Feld[x][y] = Store[x][y];
10132       Store[x][y] = 0;
10133       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10134       TEST_DrawLevelField(x, y);
10135     }
10136   }
10137 }
10138
10139 void MauerAbleger(int ax, int ay)
10140 {
10141   int element = Feld[ax][ay];
10142   int graphic = el2img(element);
10143   boolean oben_frei = FALSE, unten_frei = FALSE;
10144   boolean links_frei = FALSE, rechts_frei = FALSE;
10145   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10146   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10147   boolean new_wall = FALSE;
10148
10149   if (IS_ANIMATED(graphic))
10150     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10151
10152   if (!MovDelay[ax][ay])        /* start building new wall */
10153     MovDelay[ax][ay] = 6;
10154
10155   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10156   {
10157     MovDelay[ax][ay]--;
10158     if (MovDelay[ax][ay])
10159       return;
10160   }
10161
10162   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10163     oben_frei = TRUE;
10164   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10165     unten_frei = TRUE;
10166   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10167     links_frei = TRUE;
10168   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10169     rechts_frei = TRUE;
10170
10171   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10172       element == EL_EXPANDABLE_WALL_ANY)
10173   {
10174     if (oben_frei)
10175     {
10176       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10177       Store[ax][ay-1] = element;
10178       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10179       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10180         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10181                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10182       new_wall = TRUE;
10183     }
10184     if (unten_frei)
10185     {
10186       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10187       Store[ax][ay+1] = element;
10188       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10189       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10190         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10191                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10192       new_wall = TRUE;
10193     }
10194   }
10195
10196   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10197       element == EL_EXPANDABLE_WALL_ANY ||
10198       element == EL_EXPANDABLE_WALL ||
10199       element == EL_BD_EXPANDABLE_WALL)
10200   {
10201     if (links_frei)
10202     {
10203       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10204       Store[ax-1][ay] = element;
10205       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10206       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10207         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10208                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10209       new_wall = TRUE;
10210     }
10211
10212     if (rechts_frei)
10213     {
10214       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10215       Store[ax+1][ay] = element;
10216       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10217       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10218         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10219                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10220       new_wall = TRUE;
10221     }
10222   }
10223
10224   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10225     TEST_DrawLevelField(ax, ay);
10226
10227   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10228     oben_massiv = TRUE;
10229   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10230     unten_massiv = TRUE;
10231   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10232     links_massiv = TRUE;
10233   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10234     rechts_massiv = TRUE;
10235
10236   if (((oben_massiv && unten_massiv) ||
10237        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10238        element == EL_EXPANDABLE_WALL) &&
10239       ((links_massiv && rechts_massiv) ||
10240        element == EL_EXPANDABLE_WALL_VERTICAL))
10241     Feld[ax][ay] = EL_WALL;
10242
10243   if (new_wall)
10244     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10245 }
10246
10247 void MauerAblegerStahl(int ax, int ay)
10248 {
10249   int element = Feld[ax][ay];
10250   int graphic = el2img(element);
10251   boolean oben_frei = FALSE, unten_frei = FALSE;
10252   boolean links_frei = FALSE, rechts_frei = FALSE;
10253   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10254   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10255   boolean new_wall = FALSE;
10256
10257   if (IS_ANIMATED(graphic))
10258     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10259
10260   if (!MovDelay[ax][ay])        /* start building new wall */
10261     MovDelay[ax][ay] = 6;
10262
10263   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10264   {
10265     MovDelay[ax][ay]--;
10266     if (MovDelay[ax][ay])
10267       return;
10268   }
10269
10270   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10271     oben_frei = TRUE;
10272   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10273     unten_frei = TRUE;
10274   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10275     links_frei = TRUE;
10276   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10277     rechts_frei = TRUE;
10278
10279   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10280       element == EL_EXPANDABLE_STEELWALL_ANY)
10281   {
10282     if (oben_frei)
10283     {
10284       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10285       Store[ax][ay-1] = element;
10286       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10287       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10288         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10289                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10290       new_wall = TRUE;
10291     }
10292     if (unten_frei)
10293     {
10294       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10295       Store[ax][ay+1] = element;
10296       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10297       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10298         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10299                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10300       new_wall = TRUE;
10301     }
10302   }
10303
10304   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10305       element == EL_EXPANDABLE_STEELWALL_ANY)
10306   {
10307     if (links_frei)
10308     {
10309       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10310       Store[ax-1][ay] = element;
10311       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10312       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10313         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10314                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10315       new_wall = TRUE;
10316     }
10317
10318     if (rechts_frei)
10319     {
10320       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10321       Store[ax+1][ay] = element;
10322       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10323       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10324         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10325                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10326       new_wall = TRUE;
10327     }
10328   }
10329
10330   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10331     oben_massiv = TRUE;
10332   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10333     unten_massiv = TRUE;
10334   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10335     links_massiv = TRUE;
10336   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10337     rechts_massiv = TRUE;
10338
10339   if (((oben_massiv && unten_massiv) ||
10340        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10341       ((links_massiv && rechts_massiv) ||
10342        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10343     Feld[ax][ay] = EL_STEELWALL;
10344
10345   if (new_wall)
10346     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10347 }
10348
10349 void CheckForDragon(int x, int y)
10350 {
10351   int i, j;
10352   boolean dragon_found = FALSE;
10353   static int xy[4][2] =
10354   {
10355     { 0, -1 },
10356     { -1, 0 },
10357     { +1, 0 },
10358     { 0, +1 }
10359   };
10360
10361   for (i = 0; i < NUM_DIRECTIONS; i++)
10362   {
10363     for (j = 0; j < 4; j++)
10364     {
10365       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10366
10367       if (IN_LEV_FIELD(xx, yy) &&
10368           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10369       {
10370         if (Feld[xx][yy] == EL_DRAGON)
10371           dragon_found = TRUE;
10372       }
10373       else
10374         break;
10375     }
10376   }
10377
10378   if (!dragon_found)
10379   {
10380     for (i = 0; i < NUM_DIRECTIONS; i++)
10381     {
10382       for (j = 0; j < 3; j++)
10383       {
10384         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10385   
10386         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10387         {
10388           Feld[xx][yy] = EL_EMPTY;
10389           TEST_DrawLevelField(xx, yy);
10390         }
10391         else
10392           break;
10393       }
10394     }
10395   }
10396 }
10397
10398 static void InitBuggyBase(int x, int y)
10399 {
10400   int element = Feld[x][y];
10401   int activating_delay = FRAMES_PER_SECOND / 4;
10402
10403   ChangeDelay[x][y] =
10404     (element == EL_SP_BUGGY_BASE ?
10405      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10406      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10407      activating_delay :
10408      element == EL_SP_BUGGY_BASE_ACTIVE ?
10409      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10410 }
10411
10412 static void WarnBuggyBase(int x, int y)
10413 {
10414   int i;
10415   static int xy[4][2] =
10416   {
10417     { 0, -1 },
10418     { -1, 0 },
10419     { +1, 0 },
10420     { 0, +1 }
10421   };
10422
10423   for (i = 0; i < NUM_DIRECTIONS; i++)
10424   {
10425     int xx = x + xy[i][0];
10426     int yy = y + xy[i][1];
10427
10428     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10429     {
10430       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10431
10432       break;
10433     }
10434   }
10435 }
10436
10437 static void InitTrap(int x, int y)
10438 {
10439   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10440 }
10441
10442 static void ActivateTrap(int x, int y)
10443 {
10444   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10445 }
10446
10447 static void ChangeActiveTrap(int x, int y)
10448 {
10449   int graphic = IMG_TRAP_ACTIVE;
10450
10451   /* if new animation frame was drawn, correct crumbled sand border */
10452   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10453     TEST_DrawLevelFieldCrumbled(x, y);
10454 }
10455
10456 static int getSpecialActionElement(int element, int number, int base_element)
10457 {
10458   return (element != EL_EMPTY ? element :
10459           number != -1 ? base_element + number - 1 :
10460           EL_EMPTY);
10461 }
10462
10463 static int getModifiedActionNumber(int value_old, int operator, int operand,
10464                                    int value_min, int value_max)
10465 {
10466   int value_new = (operator == CA_MODE_SET      ? operand :
10467                    operator == CA_MODE_ADD      ? value_old + operand :
10468                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10469                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10470                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10471                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10472                    value_old);
10473
10474   return (value_new < value_min ? value_min :
10475           value_new > value_max ? value_max :
10476           value_new);
10477 }
10478
10479 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10480 {
10481   struct ElementInfo *ei = &element_info[element];
10482   struct ElementChangeInfo *change = &ei->change_page[page];
10483   int target_element = change->target_element;
10484   int action_type = change->action_type;
10485   int action_mode = change->action_mode;
10486   int action_arg = change->action_arg;
10487   int action_element = change->action_element;
10488   int i;
10489
10490   if (!change->has_action)
10491     return;
10492
10493   /* ---------- determine action paramater values -------------------------- */
10494
10495   int level_time_value =
10496     (level.time > 0 ? TimeLeft :
10497      TimePlayed);
10498
10499   int action_arg_element_raw =
10500     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10501      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10502      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10503      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10504      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10505      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10506      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10507      EL_EMPTY);
10508   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10509
10510 #if 0
10511   if (action_arg_element_raw == EL_GROUP_START)
10512     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10513 #endif
10514
10515   int action_arg_direction =
10516     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10517      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10518      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10519      change->actual_trigger_side :
10520      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10521      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10522      MV_NONE);
10523
10524   int action_arg_number_min =
10525     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10526      CA_ARG_MIN);
10527
10528   int action_arg_number_max =
10529     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10530      action_type == CA_SET_LEVEL_GEMS ? 999 :
10531      action_type == CA_SET_LEVEL_TIME ? 9999 :
10532      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10533      action_type == CA_SET_CE_VALUE ? 9999 :
10534      action_type == CA_SET_CE_SCORE ? 9999 :
10535      CA_ARG_MAX);
10536
10537   int action_arg_number_reset =
10538     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10539      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10540      action_type == CA_SET_LEVEL_TIME ? level.time :
10541      action_type == CA_SET_LEVEL_SCORE ? 0 :
10542 #if USE_NEW_CUSTOM_VALUE
10543      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10544 #else
10545      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10546 #endif
10547      action_type == CA_SET_CE_SCORE ? 0 :
10548      0);
10549
10550   int action_arg_number =
10551     (action_arg <= CA_ARG_MAX ? action_arg :
10552      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10553      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10554      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10555      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10556      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10557      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10558 #if USE_NEW_CUSTOM_VALUE
10559      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10560 #else
10561      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10562 #endif
10563      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10564      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10565      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10566      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10567      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10568      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10569      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10570      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10571      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10572      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10573      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10574      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10575      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10576      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10577      -1);
10578
10579   int action_arg_number_old =
10580     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10581      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10582      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10583      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10584      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10585      0);
10586
10587   int action_arg_number_new =
10588     getModifiedActionNumber(action_arg_number_old,
10589                             action_mode, action_arg_number,
10590                             action_arg_number_min, action_arg_number_max);
10591
10592 #if 1
10593   int trigger_player_bits =
10594     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10595      change->actual_trigger_player_bits : change->trigger_player);
10596 #else
10597   int trigger_player_bits =
10598     (change->actual_trigger_player >= EL_PLAYER_1 &&
10599      change->actual_trigger_player <= EL_PLAYER_4 ?
10600      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10601      PLAYER_BITS_ANY);
10602 #endif
10603
10604   int action_arg_player_bits =
10605     (action_arg >= CA_ARG_PLAYER_1 &&
10606      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10607      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10608      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10609      PLAYER_BITS_ANY);
10610
10611   /* ---------- execute action  -------------------------------------------- */
10612
10613   switch (action_type)
10614   {
10615     case CA_NO_ACTION:
10616     {
10617       return;
10618     }
10619
10620     /* ---------- level actions  ------------------------------------------- */
10621
10622     case CA_RESTART_LEVEL:
10623     {
10624       game.restart_level = TRUE;
10625
10626       break;
10627     }
10628
10629     case CA_SHOW_ENVELOPE:
10630     {
10631       int element = getSpecialActionElement(action_arg_element,
10632                                             action_arg_number, EL_ENVELOPE_1);
10633
10634       if (IS_ENVELOPE(element))
10635         local_player->show_envelope = element;
10636
10637       break;
10638     }
10639
10640     case CA_SET_LEVEL_TIME:
10641     {
10642       if (level.time > 0)       /* only modify limited time value */
10643       {
10644         TimeLeft = action_arg_number_new;
10645
10646 #if 1
10647         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10648
10649         DisplayGameControlValues();
10650 #else
10651         DrawGameValue_Time(TimeLeft);
10652 #endif
10653
10654         if (!TimeLeft && setup.time_limit)
10655           for (i = 0; i < MAX_PLAYERS; i++)
10656             KillPlayer(&stored_player[i]);
10657       }
10658
10659       break;
10660     }
10661
10662     case CA_SET_LEVEL_SCORE:
10663     {
10664       local_player->score = action_arg_number_new;
10665
10666 #if 1
10667       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10668
10669       DisplayGameControlValues();
10670 #else
10671       DrawGameValue_Score(local_player->score);
10672 #endif
10673
10674       break;
10675     }
10676
10677     case CA_SET_LEVEL_GEMS:
10678     {
10679       local_player->gems_still_needed = action_arg_number_new;
10680
10681 #if 1
10682       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10683
10684       DisplayGameControlValues();
10685 #else
10686       DrawGameValue_Emeralds(local_player->gems_still_needed);
10687 #endif
10688
10689       break;
10690     }
10691
10692 #if !USE_PLAYER_GRAVITY
10693     case CA_SET_LEVEL_GRAVITY:
10694     {
10695       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10696                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10697                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10698                       game.gravity);
10699       break;
10700     }
10701 #endif
10702
10703     case CA_SET_LEVEL_WIND:
10704     {
10705       game.wind_direction = action_arg_direction;
10706
10707       break;
10708     }
10709
10710     case CA_SET_LEVEL_RANDOM_SEED:
10711     {
10712 #if 1
10713       /* ensure that setting a new random seed while playing is predictable */
10714       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10715 #else
10716       InitRND(action_arg_number_new);
10717 #endif
10718
10719 #if 0
10720       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10721 #endif
10722
10723 #if 0
10724       {
10725         int i;
10726
10727         printf("::: ");
10728         for (i = 0; i < 9; i++)
10729           printf("%d, ", RND(2));
10730         printf("\n");
10731       }
10732 #endif
10733
10734       break;
10735     }
10736
10737     /* ---------- player actions  ------------------------------------------ */
10738
10739     case CA_MOVE_PLAYER:
10740     {
10741       /* automatically move to the next field in specified direction */
10742       for (i = 0; i < MAX_PLAYERS; i++)
10743         if (trigger_player_bits & (1 << i))
10744           stored_player[i].programmed_action = action_arg_direction;
10745
10746       break;
10747     }
10748
10749     case CA_EXIT_PLAYER:
10750     {
10751       for (i = 0; i < MAX_PLAYERS; i++)
10752         if (action_arg_player_bits & (1 << i))
10753           PlayerWins(&stored_player[i]);
10754
10755       break;
10756     }
10757
10758     case CA_KILL_PLAYER:
10759     {
10760       for (i = 0; i < MAX_PLAYERS; i++)
10761         if (action_arg_player_bits & (1 << i))
10762           KillPlayer(&stored_player[i]);
10763
10764       break;
10765     }
10766
10767     case CA_SET_PLAYER_KEYS:
10768     {
10769       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10770       int element = getSpecialActionElement(action_arg_element,
10771                                             action_arg_number, EL_KEY_1);
10772
10773       if (IS_KEY(element))
10774       {
10775         for (i = 0; i < MAX_PLAYERS; i++)
10776         {
10777           if (trigger_player_bits & (1 << i))
10778           {
10779             stored_player[i].key[KEY_NR(element)] = key_state;
10780
10781             DrawGameDoorValues();
10782           }
10783         }
10784       }
10785
10786       break;
10787     }
10788
10789     case CA_SET_PLAYER_SPEED:
10790     {
10791 #if 0
10792       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10793 #endif
10794
10795       for (i = 0; i < MAX_PLAYERS; i++)
10796       {
10797         if (trigger_player_bits & (1 << i))
10798         {
10799           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10800
10801           if (action_arg == CA_ARG_SPEED_FASTER &&
10802               stored_player[i].cannot_move)
10803           {
10804             action_arg_number = STEPSIZE_VERY_SLOW;
10805           }
10806           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10807                    action_arg == CA_ARG_SPEED_FASTER)
10808           {
10809             action_arg_number = 2;
10810             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10811                            CA_MODE_MULTIPLY);
10812           }
10813           else if (action_arg == CA_ARG_NUMBER_RESET)
10814           {
10815             action_arg_number = level.initial_player_stepsize[i];
10816           }
10817
10818           move_stepsize =
10819             getModifiedActionNumber(move_stepsize,
10820                                     action_mode,
10821                                     action_arg_number,
10822                                     action_arg_number_min,
10823                                     action_arg_number_max);
10824
10825           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10826         }
10827       }
10828
10829       break;
10830     }
10831
10832     case CA_SET_PLAYER_SHIELD:
10833     {
10834       for (i = 0; i < MAX_PLAYERS; i++)
10835       {
10836         if (trigger_player_bits & (1 << i))
10837         {
10838           if (action_arg == CA_ARG_SHIELD_OFF)
10839           {
10840             stored_player[i].shield_normal_time_left = 0;
10841             stored_player[i].shield_deadly_time_left = 0;
10842           }
10843           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10844           {
10845             stored_player[i].shield_normal_time_left = 999999;
10846           }
10847           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10848           {
10849             stored_player[i].shield_normal_time_left = 999999;
10850             stored_player[i].shield_deadly_time_left = 999999;
10851           }
10852         }
10853       }
10854
10855       break;
10856     }
10857
10858 #if USE_PLAYER_GRAVITY
10859     case CA_SET_PLAYER_GRAVITY:
10860     {
10861       for (i = 0; i < MAX_PLAYERS; i++)
10862       {
10863         if (trigger_player_bits & (1 << i))
10864         {
10865           stored_player[i].gravity =
10866             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10867              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10868              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10869              stored_player[i].gravity);
10870         }
10871       }
10872
10873       break;
10874     }
10875 #endif
10876
10877     case CA_SET_PLAYER_ARTWORK:
10878     {
10879       for (i = 0; i < MAX_PLAYERS; i++)
10880       {
10881         if (trigger_player_bits & (1 << i))
10882         {
10883           int artwork_element = action_arg_element;
10884
10885           if (action_arg == CA_ARG_ELEMENT_RESET)
10886             artwork_element =
10887               (level.use_artwork_element[i] ? level.artwork_element[i] :
10888                stored_player[i].element_nr);
10889
10890 #if USE_GFX_RESET_PLAYER_ARTWORK
10891           if (stored_player[i].artwork_element != artwork_element)
10892             stored_player[i].Frame = 0;
10893 #endif
10894
10895           stored_player[i].artwork_element = artwork_element;
10896
10897           SetPlayerWaiting(&stored_player[i], FALSE);
10898
10899           /* set number of special actions for bored and sleeping animation */
10900           stored_player[i].num_special_action_bored =
10901             get_num_special_action(artwork_element,
10902                                    ACTION_BORING_1, ACTION_BORING_LAST);
10903           stored_player[i].num_special_action_sleeping =
10904             get_num_special_action(artwork_element,
10905                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10906         }
10907       }
10908
10909       break;
10910     }
10911
10912     case CA_SET_PLAYER_INVENTORY:
10913     {
10914       for (i = 0; i < MAX_PLAYERS; i++)
10915       {
10916         struct PlayerInfo *player = &stored_player[i];
10917         int j, k;
10918
10919         if (trigger_player_bits & (1 << i))
10920         {
10921           int inventory_element = action_arg_element;
10922
10923           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10924               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10925               action_arg == CA_ARG_ELEMENT_ACTION)
10926           {
10927             int element = inventory_element;
10928             int collect_count = element_info[element].collect_count_initial;
10929
10930             if (!IS_CUSTOM_ELEMENT(element))
10931               collect_count = 1;
10932
10933             if (collect_count == 0)
10934               player->inventory_infinite_element = element;
10935             else
10936               for (k = 0; k < collect_count; k++)
10937                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10938                   player->inventory_element[player->inventory_size++] =
10939                     element;
10940           }
10941           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10942                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10943                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10944           {
10945             if (player->inventory_infinite_element != EL_UNDEFINED &&
10946                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10947                                      action_arg_element_raw))
10948               player->inventory_infinite_element = EL_UNDEFINED;
10949
10950             for (k = 0, j = 0; j < player->inventory_size; j++)
10951             {
10952               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10953                                         action_arg_element_raw))
10954                 player->inventory_element[k++] = player->inventory_element[j];
10955             }
10956
10957             player->inventory_size = k;
10958           }
10959           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10960           {
10961             if (player->inventory_size > 0)
10962             {
10963               for (j = 0; j < player->inventory_size - 1; j++)
10964                 player->inventory_element[j] = player->inventory_element[j + 1];
10965
10966               player->inventory_size--;
10967             }
10968           }
10969           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10970           {
10971             if (player->inventory_size > 0)
10972               player->inventory_size--;
10973           }
10974           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10975           {
10976             player->inventory_infinite_element = EL_UNDEFINED;
10977             player->inventory_size = 0;
10978           }
10979           else if (action_arg == CA_ARG_INVENTORY_RESET)
10980           {
10981             player->inventory_infinite_element = EL_UNDEFINED;
10982             player->inventory_size = 0;
10983
10984             if (level.use_initial_inventory[i])
10985             {
10986               for (j = 0; j < level.initial_inventory_size[i]; j++)
10987               {
10988                 int element = level.initial_inventory_content[i][j];
10989                 int collect_count = element_info[element].collect_count_initial;
10990
10991                 if (!IS_CUSTOM_ELEMENT(element))
10992                   collect_count = 1;
10993
10994                 if (collect_count == 0)
10995                   player->inventory_infinite_element = element;
10996                 else
10997                   for (k = 0; k < collect_count; k++)
10998                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10999                       player->inventory_element[player->inventory_size++] =
11000                         element;
11001               }
11002             }
11003           }
11004         }
11005       }
11006
11007       break;
11008     }
11009
11010     /* ---------- CE actions  ---------------------------------------------- */
11011
11012     case CA_SET_CE_VALUE:
11013     {
11014 #if USE_NEW_CUSTOM_VALUE
11015       int last_ce_value = CustomValue[x][y];
11016
11017       CustomValue[x][y] = action_arg_number_new;
11018
11019       if (CustomValue[x][y] != last_ce_value)
11020       {
11021         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11022         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11023
11024         if (CustomValue[x][y] == 0)
11025         {
11026           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11027           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11028         }
11029       }
11030 #endif
11031
11032       break;
11033     }
11034
11035     case CA_SET_CE_SCORE:
11036     {
11037 #if USE_NEW_CUSTOM_VALUE
11038       int last_ce_score = ei->collect_score;
11039
11040       ei->collect_score = action_arg_number_new;
11041
11042       if (ei->collect_score != last_ce_score)
11043       {
11044         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11045         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11046
11047         if (ei->collect_score == 0)
11048         {
11049           int xx, yy;
11050
11051           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11052           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11053
11054           /*
11055             This is a very special case that seems to be a mixture between
11056             CheckElementChange() and CheckTriggeredElementChange(): while
11057             the first one only affects single elements that are triggered
11058             directly, the second one affects multiple elements in the playfield
11059             that are triggered indirectly by another element. This is a third
11060             case: Changing the CE score always affects multiple identical CEs,
11061             so every affected CE must be checked, not only the single CE for
11062             which the CE score was changed in the first place (as every instance
11063             of that CE shares the same CE score, and therefore also can change)!
11064           */
11065           SCAN_PLAYFIELD(xx, yy)
11066           {
11067             if (Feld[xx][yy] == element)
11068               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11069                                  CE_SCORE_GETS_ZERO);
11070           }
11071         }
11072       }
11073 #endif
11074
11075       break;
11076     }
11077
11078     case CA_SET_CE_ARTWORK:
11079     {
11080       int artwork_element = action_arg_element;
11081       boolean reset_frame = FALSE;
11082       int xx, yy;
11083
11084       if (action_arg == CA_ARG_ELEMENT_RESET)
11085         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11086                            element);
11087
11088       if (ei->gfx_element != artwork_element)
11089         reset_frame = TRUE;
11090
11091       ei->gfx_element = artwork_element;
11092
11093       SCAN_PLAYFIELD(xx, yy)
11094       {
11095         if (Feld[xx][yy] == element)
11096         {
11097           if (reset_frame)
11098           {
11099             ResetGfxAnimation(xx, yy);
11100             ResetRandomAnimationValue(xx, yy);
11101           }
11102
11103           TEST_DrawLevelField(xx, yy);
11104         }
11105       }
11106
11107       break;
11108     }
11109
11110     /* ---------- engine actions  ------------------------------------------ */
11111
11112     case CA_SET_ENGINE_SCAN_MODE:
11113     {
11114       InitPlayfieldScanMode(action_arg);
11115
11116       break;
11117     }
11118
11119     default:
11120       break;
11121   }
11122 }
11123
11124 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11125 {
11126   int old_element = Feld[x][y];
11127   int new_element = GetElementFromGroupElement(element);
11128   int previous_move_direction = MovDir[x][y];
11129 #if USE_NEW_CUSTOM_VALUE
11130   int last_ce_value = CustomValue[x][y];
11131 #endif
11132   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11133   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11134   boolean add_player_onto_element = (new_element_is_player &&
11135 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11136                                      /* this breaks SnakeBite when a snake is
11137                                         halfway through a door that closes */
11138                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11139                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11140 #endif
11141                                      IS_WALKABLE(old_element));
11142
11143 #if 0
11144   /* check if element under the player changes from accessible to unaccessible
11145      (needed for special case of dropping element which then changes) */
11146   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11147       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11148   {
11149     Bang(x, y);
11150
11151     return;
11152   }
11153 #endif
11154
11155   if (!add_player_onto_element)
11156   {
11157     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11158       RemoveMovingField(x, y);
11159     else
11160       RemoveField(x, y);
11161
11162     Feld[x][y] = new_element;
11163
11164 #if !USE_GFX_RESET_GFX_ANIMATION
11165     ResetGfxAnimation(x, y);
11166     ResetRandomAnimationValue(x, y);
11167 #endif
11168
11169     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11170       MovDir[x][y] = previous_move_direction;
11171
11172 #if USE_NEW_CUSTOM_VALUE
11173     if (element_info[new_element].use_last_ce_value)
11174       CustomValue[x][y] = last_ce_value;
11175 #endif
11176
11177     InitField_WithBug1(x, y, FALSE);
11178
11179     new_element = Feld[x][y];   /* element may have changed */
11180
11181 #if USE_GFX_RESET_GFX_ANIMATION
11182     ResetGfxAnimation(x, y);
11183     ResetRandomAnimationValue(x, y);
11184 #endif
11185
11186     TEST_DrawLevelField(x, y);
11187
11188     if (GFX_CRUMBLED(new_element))
11189       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11190   }
11191
11192 #if 1
11193   /* check if element under the player changes from accessible to unaccessible
11194      (needed for special case of dropping element which then changes) */
11195   /* (must be checked after creating new element for walkable group elements) */
11196 #if USE_FIX_KILLED_BY_NON_WALKABLE
11197   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11198       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11199   {
11200     Bang(x, y);
11201
11202     return;
11203   }
11204 #else
11205   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11206       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11207   {
11208     Bang(x, y);
11209
11210     return;
11211   }
11212 #endif
11213 #endif
11214
11215   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11216   if (new_element_is_player)
11217     RelocatePlayer(x, y, new_element);
11218
11219   if (is_change)
11220     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11221
11222   TestIfBadThingTouchesPlayer(x, y);
11223   TestIfPlayerTouchesCustomElement(x, y);
11224   TestIfElementTouchesCustomElement(x, y);
11225 }
11226
11227 static void CreateField(int x, int y, int element)
11228 {
11229   CreateFieldExt(x, y, element, FALSE);
11230 }
11231
11232 static void CreateElementFromChange(int x, int y, int element)
11233 {
11234   element = GET_VALID_RUNTIME_ELEMENT(element);
11235
11236 #if USE_STOP_CHANGED_ELEMENTS
11237   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11238   {
11239     int old_element = Feld[x][y];
11240
11241     /* prevent changed element from moving in same engine frame
11242        unless both old and new element can either fall or move */
11243     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11244         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11245       Stop[x][y] = TRUE;
11246   }
11247 #endif
11248
11249   CreateFieldExt(x, y, element, TRUE);
11250 }
11251
11252 static boolean ChangeElement(int x, int y, int element, int page)
11253 {
11254   struct ElementInfo *ei = &element_info[element];
11255   struct ElementChangeInfo *change = &ei->change_page[page];
11256   int ce_value = CustomValue[x][y];
11257   int ce_score = ei->collect_score;
11258   int target_element;
11259   int old_element = Feld[x][y];
11260
11261   /* always use default change event to prevent running into a loop */
11262   if (ChangeEvent[x][y] == -1)
11263     ChangeEvent[x][y] = CE_DELAY;
11264
11265   if (ChangeEvent[x][y] == CE_DELAY)
11266   {
11267     /* reset actual trigger element, trigger player and action element */
11268     change->actual_trigger_element = EL_EMPTY;
11269     change->actual_trigger_player = EL_EMPTY;
11270     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11271     change->actual_trigger_side = CH_SIDE_NONE;
11272     change->actual_trigger_ce_value = 0;
11273     change->actual_trigger_ce_score = 0;
11274   }
11275
11276   /* do not change elements more than a specified maximum number of changes */
11277   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11278     return FALSE;
11279
11280   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11281
11282   if (change->explode)
11283   {
11284     Bang(x, y);
11285
11286     return TRUE;
11287   }
11288
11289   if (change->use_target_content)
11290   {
11291     boolean complete_replace = TRUE;
11292     boolean can_replace[3][3];
11293     int xx, yy;
11294
11295     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11296     {
11297       boolean is_empty;
11298       boolean is_walkable;
11299       boolean is_diggable;
11300       boolean is_collectible;
11301       boolean is_removable;
11302       boolean is_destructible;
11303       int ex = x + xx - 1;
11304       int ey = y + yy - 1;
11305       int content_element = change->target_content.e[xx][yy];
11306       int e;
11307
11308       can_replace[xx][yy] = TRUE;
11309
11310       if (ex == x && ey == y)   /* do not check changing element itself */
11311         continue;
11312
11313       if (content_element == EL_EMPTY_SPACE)
11314       {
11315         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11316
11317         continue;
11318       }
11319
11320       if (!IN_LEV_FIELD(ex, ey))
11321       {
11322         can_replace[xx][yy] = FALSE;
11323         complete_replace = FALSE;
11324
11325         continue;
11326       }
11327
11328       e = Feld[ex][ey];
11329
11330       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11331         e = MovingOrBlocked2Element(ex, ey);
11332
11333       is_empty = (IS_FREE(ex, ey) ||
11334                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11335
11336       is_walkable     = (is_empty || IS_WALKABLE(e));
11337       is_diggable     = (is_empty || IS_DIGGABLE(e));
11338       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11339       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11340       is_removable    = (is_diggable || is_collectible);
11341
11342       can_replace[xx][yy] =
11343         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11344           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11345           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11346           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11347           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11348           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11349          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11350
11351       if (!can_replace[xx][yy])
11352         complete_replace = FALSE;
11353     }
11354
11355     if (!change->only_if_complete || complete_replace)
11356     {
11357       boolean something_has_changed = FALSE;
11358
11359       if (change->only_if_complete && change->use_random_replace &&
11360           RND(100) < change->random_percentage)
11361         return FALSE;
11362
11363       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11364       {
11365         int ex = x + xx - 1;
11366         int ey = y + yy - 1;
11367         int content_element;
11368
11369         if (can_replace[xx][yy] && (!change->use_random_replace ||
11370                                     RND(100) < change->random_percentage))
11371         {
11372           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11373             RemoveMovingField(ex, ey);
11374
11375           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11376
11377           content_element = change->target_content.e[xx][yy];
11378           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11379                                               ce_value, ce_score);
11380
11381           CreateElementFromChange(ex, ey, target_element);
11382
11383           something_has_changed = TRUE;
11384
11385           /* for symmetry reasons, freeze newly created border elements */
11386           if (ex != x || ey != y)
11387             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11388         }
11389       }
11390
11391       if (something_has_changed)
11392       {
11393         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11394         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11395       }
11396     }
11397   }
11398   else
11399   {
11400     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11401                                         ce_value, ce_score);
11402
11403     if (element == EL_DIAGONAL_GROWING ||
11404         element == EL_DIAGONAL_SHRINKING)
11405     {
11406       target_element = Store[x][y];
11407
11408       Store[x][y] = EL_EMPTY;
11409     }
11410
11411     CreateElementFromChange(x, y, target_element);
11412
11413     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11414     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11415   }
11416
11417   /* this uses direct change before indirect change */
11418   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11419
11420   return TRUE;
11421 }
11422
11423 #if USE_NEW_DELAYED_ACTION
11424
11425 static void HandleElementChange(int x, int y, int page)
11426 {
11427   int element = MovingOrBlocked2Element(x, y);
11428   struct ElementInfo *ei = &element_info[element];
11429   struct ElementChangeInfo *change = &ei->change_page[page];
11430   boolean handle_action_before_change = FALSE;
11431
11432 #ifdef DEBUG
11433   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11434       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11435   {
11436     printf("\n\n");
11437     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11438            x, y, element, element_info[element].token_name);
11439     printf("HandleElementChange(): This should never happen!\n");
11440     printf("\n\n");
11441   }
11442 #endif
11443
11444   /* this can happen with classic bombs on walkable, changing elements */
11445   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11446   {
11447 #if 0
11448     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11449       ChangeDelay[x][y] = 0;
11450 #endif
11451
11452     return;
11453   }
11454
11455   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11456   {
11457     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11458
11459     if (change->can_change)
11460     {
11461 #if 1
11462       /* !!! not clear why graphic animation should be reset at all here !!! */
11463       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11464 #if USE_GFX_RESET_WHEN_NOT_MOVING
11465       /* when a custom element is about to change (for example by change delay),
11466          do not reset graphic animation when the custom element is moving */
11467       if (!IS_MOVING(x, y))
11468 #endif
11469       {
11470         ResetGfxAnimation(x, y);
11471         ResetRandomAnimationValue(x, y);
11472       }
11473 #endif
11474
11475       if (change->pre_change_function)
11476         change->pre_change_function(x, y);
11477     }
11478   }
11479
11480   ChangeDelay[x][y]--;
11481
11482   if (ChangeDelay[x][y] != 0)           /* continue element change */
11483   {
11484     if (change->can_change)
11485     {
11486       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11487
11488       if (IS_ANIMATED(graphic))
11489         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11490
11491       if (change->change_function)
11492         change->change_function(x, y);
11493     }
11494   }
11495   else                                  /* finish element change */
11496   {
11497     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11498     {
11499       page = ChangePage[x][y];
11500       ChangePage[x][y] = -1;
11501
11502       change = &ei->change_page[page];
11503     }
11504
11505     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11506     {
11507       ChangeDelay[x][y] = 1;            /* try change after next move step */
11508       ChangePage[x][y] = page;          /* remember page to use for change */
11509
11510       return;
11511     }
11512
11513 #if 1
11514     /* special case: set new level random seed before changing element */
11515     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11516       handle_action_before_change = TRUE;
11517
11518     if (change->has_action && handle_action_before_change)
11519       ExecuteCustomElementAction(x, y, element, page);
11520 #endif
11521
11522     if (change->can_change)
11523     {
11524       if (ChangeElement(x, y, element, page))
11525       {
11526         if (change->post_change_function)
11527           change->post_change_function(x, y);
11528       }
11529     }
11530
11531     if (change->has_action && !handle_action_before_change)
11532       ExecuteCustomElementAction(x, y, element, page);
11533   }
11534 }
11535
11536 #else
11537
11538 static void HandleElementChange(int x, int y, int page)
11539 {
11540   int element = MovingOrBlocked2Element(x, y);
11541   struct ElementInfo *ei = &element_info[element];
11542   struct ElementChangeInfo *change = &ei->change_page[page];
11543
11544 #ifdef DEBUG
11545   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11546   {
11547     printf("\n\n");
11548     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11549            x, y, element, element_info[element].token_name);
11550     printf("HandleElementChange(): This should never happen!\n");
11551     printf("\n\n");
11552   }
11553 #endif
11554
11555   /* this can happen with classic bombs on walkable, changing elements */
11556   if (!CAN_CHANGE(element))
11557   {
11558 #if 0
11559     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11560       ChangeDelay[x][y] = 0;
11561 #endif
11562
11563     return;
11564   }
11565
11566   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11567   {
11568     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11569
11570     ResetGfxAnimation(x, y);
11571     ResetRandomAnimationValue(x, y);
11572
11573     if (change->pre_change_function)
11574       change->pre_change_function(x, y);
11575   }
11576
11577   ChangeDelay[x][y]--;
11578
11579   if (ChangeDelay[x][y] != 0)           /* continue element change */
11580   {
11581     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11582
11583     if (IS_ANIMATED(graphic))
11584       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11585
11586     if (change->change_function)
11587       change->change_function(x, y);
11588   }
11589   else                                  /* finish element change */
11590   {
11591     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11592     {
11593       page = ChangePage[x][y];
11594       ChangePage[x][y] = -1;
11595
11596       change = &ei->change_page[page];
11597     }
11598
11599     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11600     {
11601       ChangeDelay[x][y] = 1;            /* try change after next move step */
11602       ChangePage[x][y] = page;          /* remember page to use for change */
11603
11604       return;
11605     }
11606
11607     if (ChangeElement(x, y, element, page))
11608     {
11609       if (change->post_change_function)
11610         change->post_change_function(x, y);
11611     }
11612   }
11613 }
11614
11615 #endif
11616
11617 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11618                                               int trigger_element,
11619                                               int trigger_event,
11620                                               int trigger_player,
11621                                               int trigger_side,
11622                                               int trigger_page)
11623 {
11624   boolean change_done_any = FALSE;
11625   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11626   int i;
11627
11628   if (!(trigger_events[trigger_element][trigger_event]))
11629     return FALSE;
11630
11631 #if 0
11632   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11633          trigger_event, recursion_loop_depth, recursion_loop_detected,
11634          recursion_loop_element, EL_NAME(recursion_loop_element));
11635 #endif
11636
11637   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11638
11639   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11640   {
11641     int element = EL_CUSTOM_START + i;
11642     boolean change_done = FALSE;
11643     int p;
11644
11645     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11646         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11647       continue;
11648
11649     for (p = 0; p < element_info[element].num_change_pages; p++)
11650     {
11651       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11652
11653       if (change->can_change_or_has_action &&
11654           change->has_event[trigger_event] &&
11655           change->trigger_side & trigger_side &&
11656           change->trigger_player & trigger_player &&
11657           change->trigger_page & trigger_page_bits &&
11658           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11659       {
11660         change->actual_trigger_element = trigger_element;
11661         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11662         change->actual_trigger_player_bits = trigger_player;
11663         change->actual_trigger_side = trigger_side;
11664         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11665         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11666
11667 #if 0
11668         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11669                element, EL_NAME(element), p);
11670 #endif
11671
11672         if ((change->can_change && !change_done) || change->has_action)
11673         {
11674           int x, y;
11675
11676           SCAN_PLAYFIELD(x, y)
11677           {
11678             if (Feld[x][y] == element)
11679             {
11680               if (change->can_change && !change_done)
11681               {
11682 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11683                 /* if element already changed in this frame, not only prevent
11684                    another element change (checked in ChangeElement()), but
11685                    also prevent additional element actions for this element */
11686
11687                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11688                     !level.use_action_after_change_bug)
11689                   continue;
11690 #endif
11691
11692 #if 0
11693                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11694                        element, EL_NAME(element), p);
11695 #endif
11696
11697                 ChangeDelay[x][y] = 1;
11698                 ChangeEvent[x][y] = trigger_event;
11699
11700                 HandleElementChange(x, y, p);
11701               }
11702 #if USE_NEW_DELAYED_ACTION
11703               else if (change->has_action)
11704               {
11705 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11706                 /* if element already changed in this frame, not only prevent
11707                    another element change (checked in ChangeElement()), but
11708                    also prevent additional element actions for this element */
11709
11710                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11711                     !level.use_action_after_change_bug)
11712                   continue;
11713 #endif
11714
11715
11716 #if 0
11717                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11718                        element, EL_NAME(element), p);
11719 #endif
11720
11721                 ExecuteCustomElementAction(x, y, element, p);
11722                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11723               }
11724 #else
11725               if (change->has_action)
11726               {
11727                 ExecuteCustomElementAction(x, y, element, p);
11728                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11729               }
11730 #endif
11731             }
11732           }
11733
11734           if (change->can_change)
11735           {
11736             change_done = TRUE;
11737             change_done_any = TRUE;
11738
11739 #if 0
11740             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11741                    element, EL_NAME(element), p);
11742 #endif
11743
11744           }
11745         }
11746       }
11747     }
11748   }
11749
11750   RECURSION_LOOP_DETECTION_END();
11751
11752   return change_done_any;
11753 }
11754
11755 static boolean CheckElementChangeExt(int x, int y,
11756                                      int element,
11757                                      int trigger_element,
11758                                      int trigger_event,
11759                                      int trigger_player,
11760                                      int trigger_side)
11761 {
11762   boolean change_done = FALSE;
11763   int p;
11764
11765   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11766       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11767     return FALSE;
11768
11769   if (Feld[x][y] == EL_BLOCKED)
11770   {
11771     Blocked2Moving(x, y, &x, &y);
11772     element = Feld[x][y];
11773   }
11774
11775 #if 0
11776   /* check if element has already changed */
11777   if (Feld[x][y] != element)
11778     return FALSE;
11779 #else
11780   /* check if element has already changed or is about to change after moving */
11781   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11782        Feld[x][y] != element) ||
11783
11784       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11785        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11786         ChangePage[x][y] != -1)))
11787     return FALSE;
11788 #endif
11789
11790 #if 0
11791   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11792          trigger_event, recursion_loop_depth, recursion_loop_detected,
11793          recursion_loop_element, EL_NAME(recursion_loop_element));
11794 #endif
11795
11796   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11797
11798 #if 0
11799   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11800 #endif
11801
11802   for (p = 0; p < element_info[element].num_change_pages; p++)
11803   {
11804     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11805
11806     /* check trigger element for all events where the element that is checked
11807        for changing interacts with a directly adjacent element -- this is
11808        different to element changes that affect other elements to change on the
11809        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11810     boolean check_trigger_element =
11811       (trigger_event == CE_TOUCHING_X ||
11812        trigger_event == CE_HITTING_X ||
11813        trigger_event == CE_HIT_BY_X ||
11814 #if 1
11815        /* this one was forgotten until 3.2.3 */
11816        trigger_event == CE_DIGGING_X);
11817 #endif
11818
11819     if (change->can_change_or_has_action &&
11820         change->has_event[trigger_event] &&
11821         change->trigger_side & trigger_side &&
11822         change->trigger_player & trigger_player &&
11823         (!check_trigger_element ||
11824          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11825     {
11826       change->actual_trigger_element = trigger_element;
11827       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11828       change->actual_trigger_player_bits = trigger_player;
11829       change->actual_trigger_side = trigger_side;
11830       change->actual_trigger_ce_value = CustomValue[x][y];
11831       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11832
11833       /* special case: trigger element not at (x,y) position for some events */
11834       if (check_trigger_element)
11835       {
11836         static struct
11837         {
11838           int dx, dy;
11839         } move_xy[] =
11840           {
11841             {  0,  0 },
11842             { -1,  0 },
11843             { +1,  0 },
11844             {  0,  0 },
11845             {  0, -1 },
11846             {  0,  0 }, { 0, 0 }, { 0, 0 },
11847             {  0, +1 }
11848           };
11849
11850         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11851         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11852
11853         change->actual_trigger_ce_value = CustomValue[xx][yy];
11854         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11855       }
11856
11857       if (change->can_change && !change_done)
11858       {
11859         ChangeDelay[x][y] = 1;
11860         ChangeEvent[x][y] = trigger_event;
11861
11862         HandleElementChange(x, y, p);
11863
11864         change_done = TRUE;
11865       }
11866 #if USE_NEW_DELAYED_ACTION
11867       else if (change->has_action)
11868       {
11869         ExecuteCustomElementAction(x, y, element, p);
11870         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11871       }
11872 #else
11873       if (change->has_action)
11874       {
11875         ExecuteCustomElementAction(x, y, element, p);
11876         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11877       }
11878 #endif
11879     }
11880   }
11881
11882   RECURSION_LOOP_DETECTION_END();
11883
11884   return change_done;
11885 }
11886
11887 static void PlayPlayerSound(struct PlayerInfo *player)
11888 {
11889   int jx = player->jx, jy = player->jy;
11890   int sound_element = player->artwork_element;
11891   int last_action = player->last_action_waiting;
11892   int action = player->action_waiting;
11893
11894   if (player->is_waiting)
11895   {
11896     if (action != last_action)
11897       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11898     else
11899       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11900   }
11901   else
11902   {
11903     if (action != last_action)
11904       StopSound(element_info[sound_element].sound[last_action]);
11905
11906     if (last_action == ACTION_SLEEPING)
11907       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11908   }
11909 }
11910
11911 static void PlayAllPlayersSound()
11912 {
11913   int i;
11914
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916     if (stored_player[i].active)
11917       PlayPlayerSound(&stored_player[i]);
11918 }
11919
11920 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11921 {
11922   boolean last_waiting = player->is_waiting;
11923   int move_dir = player->MovDir;
11924
11925   player->dir_waiting = move_dir;
11926   player->last_action_waiting = player->action_waiting;
11927
11928   if (is_waiting)
11929   {
11930     if (!last_waiting)          /* not waiting -> waiting */
11931     {
11932       player->is_waiting = TRUE;
11933
11934       player->frame_counter_bored =
11935         FrameCounter +
11936         game.player_boring_delay_fixed +
11937         GetSimpleRandom(game.player_boring_delay_random);
11938       player->frame_counter_sleeping =
11939         FrameCounter +
11940         game.player_sleeping_delay_fixed +
11941         GetSimpleRandom(game.player_sleeping_delay_random);
11942
11943       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11944     }
11945
11946     if (game.player_sleeping_delay_fixed +
11947         game.player_sleeping_delay_random > 0 &&
11948         player->anim_delay_counter == 0 &&
11949         player->post_delay_counter == 0 &&
11950         FrameCounter >= player->frame_counter_sleeping)
11951       player->is_sleeping = TRUE;
11952     else if (game.player_boring_delay_fixed +
11953              game.player_boring_delay_random > 0 &&
11954              FrameCounter >= player->frame_counter_bored)
11955       player->is_bored = TRUE;
11956
11957     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11958                               player->is_bored ? ACTION_BORING :
11959                               ACTION_WAITING);
11960
11961     if (player->is_sleeping && player->use_murphy)
11962     {
11963       /* special case for sleeping Murphy when leaning against non-free tile */
11964
11965       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11966           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11967            !IS_MOVING(player->jx - 1, player->jy)))
11968         move_dir = MV_LEFT;
11969       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11970                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11971                 !IS_MOVING(player->jx + 1, player->jy)))
11972         move_dir = MV_RIGHT;
11973       else
11974         player->is_sleeping = FALSE;
11975
11976       player->dir_waiting = move_dir;
11977     }
11978
11979     if (player->is_sleeping)
11980     {
11981       if (player->num_special_action_sleeping > 0)
11982       {
11983         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11984         {
11985           int last_special_action = player->special_action_sleeping;
11986           int num_special_action = player->num_special_action_sleeping;
11987           int special_action =
11988             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11989              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11990              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11991              last_special_action + 1 : ACTION_SLEEPING);
11992           int special_graphic =
11993             el_act_dir2img(player->artwork_element, special_action, move_dir);
11994
11995           player->anim_delay_counter =
11996             graphic_info[special_graphic].anim_delay_fixed +
11997             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11998           player->post_delay_counter =
11999             graphic_info[special_graphic].post_delay_fixed +
12000             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12001
12002           player->special_action_sleeping = special_action;
12003         }
12004
12005         if (player->anim_delay_counter > 0)
12006         {
12007           player->action_waiting = player->special_action_sleeping;
12008           player->anim_delay_counter--;
12009         }
12010         else if (player->post_delay_counter > 0)
12011         {
12012           player->post_delay_counter--;
12013         }
12014       }
12015     }
12016     else if (player->is_bored)
12017     {
12018       if (player->num_special_action_bored > 0)
12019       {
12020         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12021         {
12022           int special_action =
12023             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12024           int special_graphic =
12025             el_act_dir2img(player->artwork_element, special_action, move_dir);
12026
12027           player->anim_delay_counter =
12028             graphic_info[special_graphic].anim_delay_fixed +
12029             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12030           player->post_delay_counter =
12031             graphic_info[special_graphic].post_delay_fixed +
12032             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12033
12034           player->special_action_bored = special_action;
12035         }
12036
12037         if (player->anim_delay_counter > 0)
12038         {
12039           player->action_waiting = player->special_action_bored;
12040           player->anim_delay_counter--;
12041         }
12042         else if (player->post_delay_counter > 0)
12043         {
12044           player->post_delay_counter--;
12045         }
12046       }
12047     }
12048   }
12049   else if (last_waiting)        /* waiting -> not waiting */
12050   {
12051     player->is_waiting = FALSE;
12052     player->is_bored = FALSE;
12053     player->is_sleeping = FALSE;
12054
12055     player->frame_counter_bored = -1;
12056     player->frame_counter_sleeping = -1;
12057
12058     player->anim_delay_counter = 0;
12059     player->post_delay_counter = 0;
12060
12061     player->dir_waiting = player->MovDir;
12062     player->action_waiting = ACTION_DEFAULT;
12063
12064     player->special_action_bored = ACTION_DEFAULT;
12065     player->special_action_sleeping = ACTION_DEFAULT;
12066   }
12067 }
12068
12069 static void CheckSingleStepMode(struct PlayerInfo *player)
12070 {
12071   if (tape.single_step && tape.recording && !tape.pausing)
12072   {
12073     /* as it is called "single step mode", just return to pause mode when the
12074        player stopped moving after one tile (or never starts moving at all) */
12075     if (!player->is_moving && !player->is_pushing)
12076     {
12077       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12078       SnapField(player, 0, 0);                  /* stop snapping */
12079     }
12080   }
12081 }
12082
12083 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12084 {
12085   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12086   int left      = player_action & JOY_LEFT;
12087   int right     = player_action & JOY_RIGHT;
12088   int up        = player_action & JOY_UP;
12089   int down      = player_action & JOY_DOWN;
12090   int button1   = player_action & JOY_BUTTON_1;
12091   int button2   = player_action & JOY_BUTTON_2;
12092   int dx        = (left ? -1 : right ? 1 : 0);
12093   int dy        = (up   ? -1 : down  ? 1 : 0);
12094
12095   if (!player->active || tape.pausing)
12096     return 0;
12097
12098   if (player_action)
12099   {
12100     if (button1)
12101       snapped = SnapField(player, dx, dy);
12102     else
12103     {
12104       if (button2)
12105         dropped = DropElement(player);
12106
12107       moved = MovePlayer(player, dx, dy);
12108     }
12109
12110     CheckSingleStepMode(player);
12111
12112     SetPlayerWaiting(player, FALSE);
12113
12114     return player_action;
12115   }
12116   else
12117   {
12118     /* no actions for this player (no input at player's configured device) */
12119
12120     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12121     SnapField(player, 0, 0);
12122     CheckGravityMovementWhenNotMoving(player);
12123
12124     if (player->MovPos == 0)
12125       SetPlayerWaiting(player, TRUE);
12126
12127     if (player->MovPos == 0)    /* needed for tape.playing */
12128       player->is_moving = FALSE;
12129
12130     player->is_dropping = FALSE;
12131     player->is_dropping_pressed = FALSE;
12132     player->drop_pressed_delay = 0;
12133
12134     CheckSingleStepMode(player);
12135
12136     return 0;
12137   }
12138 }
12139
12140 static void CheckLevelTime()
12141 {
12142   int i;
12143
12144   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12145   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12146   {
12147     if (level.native_em_level->lev->home == 0)  /* all players at home */
12148     {
12149       PlayerWins(local_player);
12150
12151       AllPlayersGone = TRUE;
12152
12153       level.native_em_level->lev->home = -1;
12154     }
12155
12156     if (level.native_em_level->ply[0]->alive == 0 &&
12157         level.native_em_level->ply[1]->alive == 0 &&
12158         level.native_em_level->ply[2]->alive == 0 &&
12159         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12160       AllPlayersGone = TRUE;
12161   }
12162   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12163   {
12164     if (game_sp.LevelSolved &&
12165         !game_sp.GameOver)                              /* game won */
12166     {
12167       PlayerWins(local_player);
12168
12169       game_sp.GameOver = TRUE;
12170
12171       AllPlayersGone = TRUE;
12172     }
12173
12174     if (game_sp.GameOver)                               /* game lost */
12175       AllPlayersGone = TRUE;
12176   }
12177
12178   if (TimeFrames >= FRAMES_PER_SECOND)
12179   {
12180     TimeFrames = 0;
12181     TapeTime++;
12182
12183     for (i = 0; i < MAX_PLAYERS; i++)
12184     {
12185       struct PlayerInfo *player = &stored_player[i];
12186
12187       if (SHIELD_ON(player))
12188       {
12189         player->shield_normal_time_left--;
12190
12191         if (player->shield_deadly_time_left > 0)
12192           player->shield_deadly_time_left--;
12193       }
12194     }
12195
12196     if (!local_player->LevelSolved && !level.use_step_counter)
12197     {
12198       TimePlayed++;
12199
12200       if (TimeLeft > 0)
12201       {
12202         TimeLeft--;
12203
12204         if (TimeLeft <= 10 && setup.time_limit)
12205           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12206
12207 #if 1
12208         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12209
12210         DisplayGameControlValues();
12211 #else
12212         DrawGameValue_Time(TimeLeft);
12213 #endif
12214
12215         if (!TimeLeft && setup.time_limit)
12216         {
12217           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12218             level.native_em_level->lev->killed_out_of_time = TRUE;
12219           else
12220             for (i = 0; i < MAX_PLAYERS; i++)
12221               KillPlayer(&stored_player[i]);
12222         }
12223       }
12224 #if 1
12225       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12226       {
12227         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12228
12229         DisplayGameControlValues();
12230       }
12231 #else
12232       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12233         DrawGameValue_Time(TimePlayed);
12234 #endif
12235
12236       level.native_em_level->lev->time =
12237         (level.time == 0 ? TimePlayed : TimeLeft);
12238     }
12239
12240     if (tape.recording || tape.playing)
12241       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12242   }
12243
12244 #if 1
12245   UpdateAndDisplayGameControlValues();
12246 #else
12247   UpdateGameDoorValues();
12248   DrawGameDoorValues();
12249 #endif
12250 }
12251
12252 void AdvanceFrameAndPlayerCounters(int player_nr)
12253 {
12254   int i;
12255
12256   /* advance frame counters (global frame counter and time frame counter) */
12257   FrameCounter++;
12258   TimeFrames++;
12259
12260   /* advance player counters (counters for move delay, move animation etc.) */
12261   for (i = 0; i < MAX_PLAYERS; i++)
12262   {
12263     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12264     int move_delay_value = stored_player[i].move_delay_value;
12265     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12266
12267     if (!advance_player_counters)       /* not all players may be affected */
12268       continue;
12269
12270 #if USE_NEW_PLAYER_ANIM
12271     if (move_frames == 0)       /* less than one move per game frame */
12272     {
12273       int stepsize = TILEX / move_delay_value;
12274       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12275       int count = (stored_player[i].is_moving ?
12276                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12277
12278       if (count % delay == 0)
12279         move_frames = 1;
12280     }
12281 #endif
12282
12283     stored_player[i].Frame += move_frames;
12284
12285     if (stored_player[i].MovPos != 0)
12286       stored_player[i].StepFrame += move_frames;
12287
12288     if (stored_player[i].move_delay > 0)
12289       stored_player[i].move_delay--;
12290
12291     /* due to bugs in previous versions, counter must count up, not down */
12292     if (stored_player[i].push_delay != -1)
12293       stored_player[i].push_delay++;
12294
12295     if (stored_player[i].drop_delay > 0)
12296       stored_player[i].drop_delay--;
12297
12298     if (stored_player[i].is_dropping_pressed)
12299       stored_player[i].drop_pressed_delay++;
12300   }
12301 }
12302
12303 void StartGameActions(boolean init_network_game, boolean record_tape,
12304                       long random_seed)
12305 {
12306   unsigned long new_random_seed = InitRND(random_seed);
12307
12308   if (record_tape)
12309     TapeStartRecording(new_random_seed);
12310
12311 #if defined(NETWORK_AVALIABLE)
12312   if (init_network_game)
12313   {
12314     SendToServer_StartPlaying();
12315
12316     return;
12317   }
12318 #endif
12319
12320   InitGame();
12321 }
12322
12323 void GameActions()
12324 {
12325   static unsigned long game_frame_delay = 0;
12326   unsigned long game_frame_delay_value;
12327   byte *recorded_player_action;
12328   byte summarized_player_action = 0;
12329   byte tape_action[MAX_PLAYERS];
12330   int i;
12331
12332   /* detect endless loops, caused by custom element programming */
12333   if (recursion_loop_detected && recursion_loop_depth == 0)
12334   {
12335     char *message = getStringCat3("Internal Error ! Element ",
12336                                   EL_NAME(recursion_loop_element),
12337                                   " caused endless loop ! Quit the game ?");
12338
12339     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12340           EL_NAME(recursion_loop_element));
12341
12342     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12343
12344     recursion_loop_detected = FALSE;    /* if game should be continued */
12345
12346     free(message);
12347
12348     return;
12349   }
12350
12351   if (game.restart_level)
12352     StartGameActions(options.network, setup.autorecord, level.random_seed);
12353
12354   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12355   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12356   {
12357     if (level.native_em_level->lev->home == 0)  /* all players at home */
12358     {
12359       PlayerWins(local_player);
12360
12361       AllPlayersGone = TRUE;
12362
12363       level.native_em_level->lev->home = -1;
12364     }
12365
12366     if (level.native_em_level->ply[0]->alive == 0 &&
12367         level.native_em_level->ply[1]->alive == 0 &&
12368         level.native_em_level->ply[2]->alive == 0 &&
12369         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12370       AllPlayersGone = TRUE;
12371   }
12372   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12373   {
12374     if (game_sp.LevelSolved &&
12375         !game_sp.GameOver)                              /* game won */
12376     {
12377       PlayerWins(local_player);
12378
12379       game_sp.GameOver = TRUE;
12380
12381       AllPlayersGone = TRUE;
12382     }
12383
12384     if (game_sp.GameOver)                               /* game lost */
12385       AllPlayersGone = TRUE;
12386   }
12387
12388   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12389     GameWon();
12390
12391   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12392     TapeStop();
12393
12394   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12395     return;
12396
12397   game_frame_delay_value =
12398     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12399
12400   if (tape.playing && tape.warp_forward && !tape.pausing)
12401     game_frame_delay_value = 0;
12402
12403   /* ---------- main game synchronization point ---------- */
12404
12405   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12406
12407   if (network_playing && !network_player_action_received)
12408   {
12409     /* try to get network player actions in time */
12410
12411 #if defined(NETWORK_AVALIABLE)
12412     /* last chance to get network player actions without main loop delay */
12413     HandleNetworking();
12414 #endif
12415
12416     /* game was quit by network peer */
12417     if (game_status != GAME_MODE_PLAYING)
12418       return;
12419
12420     if (!network_player_action_received)
12421       return;           /* failed to get network player actions in time */
12422
12423     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12424   }
12425
12426   if (tape.pausing)
12427     return;
12428
12429   /* at this point we know that we really continue executing the game */
12430
12431   network_player_action_received = FALSE;
12432
12433   /* when playing tape, read previously recorded player input from tape data */
12434   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12435
12436 #if 1
12437   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12438   if (tape.pausing)
12439     return;
12440 #endif
12441
12442   if (tape.set_centered_player)
12443   {
12444     game.centered_player_nr_next = tape.centered_player_nr_next;
12445     game.set_centered_player = TRUE;
12446   }
12447
12448   for (i = 0; i < MAX_PLAYERS; i++)
12449   {
12450     summarized_player_action |= stored_player[i].action;
12451
12452     if (!network_playing)
12453       stored_player[i].effective_action = stored_player[i].action;
12454   }
12455
12456 #if defined(NETWORK_AVALIABLE)
12457   if (network_playing)
12458     SendToServer_MovePlayer(summarized_player_action);
12459 #endif
12460
12461   if (!options.network && !setup.team_mode)
12462     local_player->effective_action = summarized_player_action;
12463
12464   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12465   {
12466     for (i = 0; i < MAX_PLAYERS; i++)
12467       stored_player[i].effective_action =
12468         (i == game.centered_player_nr ? summarized_player_action : 0);
12469   }
12470
12471   if (recorded_player_action != NULL)
12472     for (i = 0; i < MAX_PLAYERS; i++)
12473       stored_player[i].effective_action = recorded_player_action[i];
12474
12475   for (i = 0; i < MAX_PLAYERS; i++)
12476   {
12477     tape_action[i] = stored_player[i].effective_action;
12478
12479     /* (this can only happen in the R'n'D game engine) */
12480     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12481       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12482   }
12483
12484   /* only record actions from input devices, but not programmed actions */
12485   if (tape.recording)
12486     TapeRecordAction(tape_action);
12487
12488 #if USE_NEW_PLAYER_ASSIGNMENTS
12489   {
12490     byte mapped_action[MAX_PLAYERS];
12491
12492     for (i = 0; i < MAX_PLAYERS; i++)
12493       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12494
12495     for (i = 0; i < MAX_PLAYERS; i++)
12496       stored_player[i].effective_action = mapped_action[i];
12497   }
12498 #endif
12499
12500   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12501   {
12502     GameActions_EM_Main();
12503   }
12504   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12505   {
12506     GameActions_SP_Main();
12507   }
12508   else
12509   {
12510     GameActions_RND();
12511   }
12512 }
12513
12514 void GameActions_EM_Main()
12515 {
12516   byte effective_action[MAX_PLAYERS];
12517   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12518   int i;
12519
12520   for (i = 0; i < MAX_PLAYERS; i++)
12521     effective_action[i] = stored_player[i].effective_action;
12522
12523   GameActions_EM(effective_action, warp_mode);
12524
12525   CheckLevelTime();
12526
12527   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12528 }
12529
12530 void GameActions_SP_Main()
12531 {
12532   byte effective_action[MAX_PLAYERS];
12533   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12534   int i;
12535
12536   for (i = 0; i < MAX_PLAYERS; i++)
12537     effective_action[i] = stored_player[i].effective_action;
12538
12539   GameActions_SP(effective_action, warp_mode);
12540
12541   CheckLevelTime();
12542
12543   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12544 }
12545
12546 void GameActions_RND()
12547 {
12548   int magic_wall_x = 0, magic_wall_y = 0;
12549   int i, x, y, element, graphic;
12550
12551   InitPlayfieldScanModeVars();
12552
12553 #if USE_ONE_MORE_CHANGE_PER_FRAME
12554   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12555   {
12556     SCAN_PLAYFIELD(x, y)
12557     {
12558       ChangeCount[x][y] = 0;
12559       ChangeEvent[x][y] = -1;
12560     }
12561   }
12562 #endif
12563
12564   if (game.set_centered_player)
12565   {
12566     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12567
12568     /* switching to "all players" only possible if all players fit to screen */
12569     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12570     {
12571       game.centered_player_nr_next = game.centered_player_nr;
12572       game.set_centered_player = FALSE;
12573     }
12574
12575     /* do not switch focus to non-existing (or non-active) player */
12576     if (game.centered_player_nr_next >= 0 &&
12577         !stored_player[game.centered_player_nr_next].active)
12578     {
12579       game.centered_player_nr_next = game.centered_player_nr;
12580       game.set_centered_player = FALSE;
12581     }
12582   }
12583
12584   if (game.set_centered_player &&
12585       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12586   {
12587     int sx, sy;
12588
12589     if (game.centered_player_nr_next == -1)
12590     {
12591       setScreenCenteredToAllPlayers(&sx, &sy);
12592     }
12593     else
12594     {
12595       sx = stored_player[game.centered_player_nr_next].jx;
12596       sy = stored_player[game.centered_player_nr_next].jy;
12597     }
12598
12599     game.centered_player_nr = game.centered_player_nr_next;
12600     game.set_centered_player = FALSE;
12601
12602     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12603     DrawGameDoorValues();
12604   }
12605
12606   for (i = 0; i < MAX_PLAYERS; i++)
12607   {
12608     int actual_player_action = stored_player[i].effective_action;
12609
12610 #if 1
12611     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12612        - rnd_equinox_tetrachloride 048
12613        - rnd_equinox_tetrachloride_ii 096
12614        - rnd_emanuel_schmieg 002
12615        - doctor_sloan_ww 001, 020
12616     */
12617     if (stored_player[i].MovPos == 0)
12618       CheckGravityMovement(&stored_player[i]);
12619 #endif
12620
12621     /* overwrite programmed action with tape action */
12622     if (stored_player[i].programmed_action)
12623       actual_player_action = stored_player[i].programmed_action;
12624
12625     PlayerActions(&stored_player[i], actual_player_action);
12626
12627     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12628   }
12629
12630   ScrollScreen(NULL, SCROLL_GO_ON);
12631
12632   /* for backwards compatibility, the following code emulates a fixed bug that
12633      occured when pushing elements (causing elements that just made their last
12634      pushing step to already (if possible) make their first falling step in the
12635      same game frame, which is bad); this code is also needed to use the famous
12636      "spring push bug" which is used in older levels and might be wanted to be
12637      used also in newer levels, but in this case the buggy pushing code is only
12638      affecting the "spring" element and no other elements */
12639
12640   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12641   {
12642     for (i = 0; i < MAX_PLAYERS; i++)
12643     {
12644       struct PlayerInfo *player = &stored_player[i];
12645       int x = player->jx;
12646       int y = player->jy;
12647
12648       if (player->active && player->is_pushing && player->is_moving &&
12649           IS_MOVING(x, y) &&
12650           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12651            Feld[x][y] == EL_SPRING))
12652       {
12653         ContinueMoving(x, y);
12654
12655         /* continue moving after pushing (this is actually a bug) */
12656         if (!IS_MOVING(x, y))
12657           Stop[x][y] = FALSE;
12658       }
12659     }
12660   }
12661
12662 #if 0
12663   debug_print_timestamp(0, "start main loop profiling");
12664 #endif
12665
12666   SCAN_PLAYFIELD(x, y)
12667   {
12668     ChangeCount[x][y] = 0;
12669     ChangeEvent[x][y] = -1;
12670
12671     /* this must be handled before main playfield loop */
12672     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12673     {
12674       MovDelay[x][y]--;
12675       if (MovDelay[x][y] <= 0)
12676         RemoveField(x, y);
12677     }
12678
12679 #if USE_NEW_SNAP_DELAY
12680     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12681     {
12682       MovDelay[x][y]--;
12683       if (MovDelay[x][y] <= 0)
12684       {
12685         RemoveField(x, y);
12686         TEST_DrawLevelField(x, y);
12687
12688         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12689       }
12690     }
12691 #endif
12692
12693 #if DEBUG
12694     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12695     {
12696       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12697       printf("GameActions(): This should never happen!\n");
12698
12699       ChangePage[x][y] = -1;
12700     }
12701 #endif
12702
12703     Stop[x][y] = FALSE;
12704     if (WasJustMoving[x][y] > 0)
12705       WasJustMoving[x][y]--;
12706     if (WasJustFalling[x][y] > 0)
12707       WasJustFalling[x][y]--;
12708     if (CheckCollision[x][y] > 0)
12709       CheckCollision[x][y]--;
12710     if (CheckImpact[x][y] > 0)
12711       CheckImpact[x][y]--;
12712
12713     GfxFrame[x][y]++;
12714
12715     /* reset finished pushing action (not done in ContinueMoving() to allow
12716        continuous pushing animation for elements with zero push delay) */
12717     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12718     {
12719       ResetGfxAnimation(x, y);
12720       TEST_DrawLevelField(x, y);
12721     }
12722
12723 #if DEBUG
12724     if (IS_BLOCKED(x, y))
12725     {
12726       int oldx, oldy;
12727
12728       Blocked2Moving(x, y, &oldx, &oldy);
12729       if (!IS_MOVING(oldx, oldy))
12730       {
12731         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12732         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12733         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12734         printf("GameActions(): This should never happen!\n");
12735       }
12736     }
12737 #endif
12738   }
12739
12740 #if 0
12741   debug_print_timestamp(0, "- time for pre-main loop:");
12742 #endif
12743
12744 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12745   SCAN_PLAYFIELD(x, y)
12746   {
12747     element = Feld[x][y];
12748     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12749
12750 #if 1
12751     {
12752 #if 1
12753       int element2 = element;
12754       int graphic2 = graphic;
12755 #else
12756       int element2 = Feld[x][y];
12757       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12758 #endif
12759       int last_gfx_frame = GfxFrame[x][y];
12760
12761       if (graphic_info[graphic2].anim_global_sync)
12762         GfxFrame[x][y] = FrameCounter;
12763       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12764         GfxFrame[x][y] = CustomValue[x][y];
12765       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12766         GfxFrame[x][y] = element_info[element2].collect_score;
12767       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12768         GfxFrame[x][y] = ChangeDelay[x][y];
12769
12770       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12771         DrawLevelGraphicAnimation(x, y, graphic2);
12772     }
12773 #else
12774     ResetGfxFrame(x, y, TRUE);
12775 #endif
12776
12777 #if 1
12778     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12779         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12780       ResetRandomAnimationValue(x, y);
12781 #endif
12782
12783 #if 1
12784     SetRandomAnimationValue(x, y);
12785 #endif
12786
12787 #if 1
12788     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12789 #endif
12790   }
12791 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12792
12793 #if 0
12794   debug_print_timestamp(0, "- time for TEST loop:     -->");
12795 #endif
12796
12797   SCAN_PLAYFIELD(x, y)
12798   {
12799     element = Feld[x][y];
12800     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12801
12802     ResetGfxFrame(x, y, TRUE);
12803
12804     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12805         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12806       ResetRandomAnimationValue(x, y);
12807
12808     SetRandomAnimationValue(x, y);
12809
12810     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12811
12812     if (IS_INACTIVE(element))
12813     {
12814       if (IS_ANIMATED(graphic))
12815         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12816
12817       continue;
12818     }
12819
12820     /* this may take place after moving, so 'element' may have changed */
12821     if (IS_CHANGING(x, y) &&
12822         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12823     {
12824       int page = element_info[element].event_page_nr[CE_DELAY];
12825
12826 #if 1
12827       HandleElementChange(x, y, page);
12828 #else
12829       if (CAN_CHANGE(element))
12830         HandleElementChange(x, y, page);
12831
12832       if (HAS_ACTION(element))
12833         ExecuteCustomElementAction(x, y, element, page);
12834 #endif
12835
12836       element = Feld[x][y];
12837       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12838     }
12839
12840 #if 0   // ---------------------------------------------------------------------
12841
12842     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12843     {
12844       StartMoving(x, y);
12845
12846       element = Feld[x][y];
12847       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12848
12849       if (IS_ANIMATED(graphic) &&
12850           !IS_MOVING(x, y) &&
12851           !Stop[x][y])
12852         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12853
12854       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12855         TEST_DrawTwinkleOnField(x, y);
12856     }
12857     else if (IS_MOVING(x, y))
12858       ContinueMoving(x, y);
12859     else
12860     {
12861       switch (element)
12862       {
12863         case EL_ACID:
12864         case EL_EXIT_OPEN:
12865         case EL_EM_EXIT_OPEN:
12866         case EL_SP_EXIT_OPEN:
12867         case EL_STEEL_EXIT_OPEN:
12868         case EL_EM_STEEL_EXIT_OPEN:
12869         case EL_SP_TERMINAL:
12870         case EL_SP_TERMINAL_ACTIVE:
12871         case EL_EXTRA_TIME:
12872         case EL_SHIELD_NORMAL:
12873         case EL_SHIELD_DEADLY:
12874           if (IS_ANIMATED(graphic))
12875             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12876           break;
12877
12878         case EL_DYNAMITE_ACTIVE:
12879         case EL_EM_DYNAMITE_ACTIVE:
12880         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12881         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12882         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12883         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12884         case EL_SP_DISK_RED_ACTIVE:
12885           CheckDynamite(x, y);
12886           break;
12887
12888         case EL_AMOEBA_GROWING:
12889           AmoebeWaechst(x, y);
12890           break;
12891
12892         case EL_AMOEBA_SHRINKING:
12893           AmoebaDisappearing(x, y);
12894           break;
12895
12896 #if !USE_NEW_AMOEBA_CODE
12897         case EL_AMOEBA_WET:
12898         case EL_AMOEBA_DRY:
12899         case EL_AMOEBA_FULL:
12900         case EL_BD_AMOEBA:
12901         case EL_EMC_DRIPPER:
12902           AmoebeAbleger(x, y);
12903           break;
12904 #endif
12905
12906         case EL_GAME_OF_LIFE:
12907         case EL_BIOMAZE:
12908           Life(x, y);
12909           break;
12910
12911         case EL_EXIT_CLOSED:
12912           CheckExit(x, y);
12913           break;
12914
12915         case EL_EM_EXIT_CLOSED:
12916           CheckExitEM(x, y);
12917           break;
12918
12919         case EL_STEEL_EXIT_CLOSED:
12920           CheckExitSteel(x, y);
12921           break;
12922
12923         case EL_EM_STEEL_EXIT_CLOSED:
12924           CheckExitSteelEM(x, y);
12925           break;
12926
12927         case EL_SP_EXIT_CLOSED:
12928           CheckExitSP(x, y);
12929           break;
12930
12931         case EL_EXPANDABLE_WALL_GROWING:
12932         case EL_EXPANDABLE_STEELWALL_GROWING:
12933           MauerWaechst(x, y);
12934           break;
12935
12936         case EL_EXPANDABLE_WALL:
12937         case EL_EXPANDABLE_WALL_HORIZONTAL:
12938         case EL_EXPANDABLE_WALL_VERTICAL:
12939         case EL_EXPANDABLE_WALL_ANY:
12940         case EL_BD_EXPANDABLE_WALL:
12941           MauerAbleger(x, y);
12942           break;
12943
12944         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12945         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12946         case EL_EXPANDABLE_STEELWALL_ANY:
12947           MauerAblegerStahl(x, y);
12948           break;
12949
12950         case EL_FLAMES:
12951           CheckForDragon(x, y);
12952           break;
12953
12954         case EL_EXPLOSION:
12955           break;
12956
12957         case EL_ELEMENT_SNAPPING:
12958         case EL_DIAGONAL_SHRINKING:
12959         case EL_DIAGONAL_GROWING:
12960         {
12961           graphic =
12962             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12963
12964           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12965           break;
12966         }
12967
12968         default:
12969           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12970             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12971           break;
12972       }
12973     }
12974
12975 #else   // ---------------------------------------------------------------------
12976
12977     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12978     {
12979       StartMoving(x, y);
12980
12981       element = Feld[x][y];
12982       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12983
12984       if (IS_ANIMATED(graphic) &&
12985           !IS_MOVING(x, y) &&
12986           !Stop[x][y])
12987         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12988
12989       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12990         TEST_DrawTwinkleOnField(x, y);
12991     }
12992     else if ((element == EL_ACID ||
12993               element == EL_EXIT_OPEN ||
12994               element == EL_EM_EXIT_OPEN ||
12995               element == EL_SP_EXIT_OPEN ||
12996               element == EL_STEEL_EXIT_OPEN ||
12997               element == EL_EM_STEEL_EXIT_OPEN ||
12998               element == EL_SP_TERMINAL ||
12999               element == EL_SP_TERMINAL_ACTIVE ||
13000               element == EL_EXTRA_TIME ||
13001               element == EL_SHIELD_NORMAL ||
13002               element == EL_SHIELD_DEADLY) &&
13003              IS_ANIMATED(graphic))
13004       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13005     else if (IS_MOVING(x, y))
13006       ContinueMoving(x, y);
13007     else if (IS_ACTIVE_BOMB(element))
13008       CheckDynamite(x, y);
13009     else if (element == EL_AMOEBA_GROWING)
13010       AmoebeWaechst(x, y);
13011     else if (element == EL_AMOEBA_SHRINKING)
13012       AmoebaDisappearing(x, y);
13013
13014 #if !USE_NEW_AMOEBA_CODE
13015     else if (IS_AMOEBALIVE(element))
13016       AmoebeAbleger(x, y);
13017 #endif
13018
13019     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13020       Life(x, y);
13021     else if (element == EL_EXIT_CLOSED)
13022       CheckExit(x, y);
13023     else if (element == EL_EM_EXIT_CLOSED)
13024       CheckExitEM(x, y);
13025     else if (element == EL_STEEL_EXIT_CLOSED)
13026       CheckExitSteel(x, y);
13027     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13028       CheckExitSteelEM(x, y);
13029     else if (element == EL_SP_EXIT_CLOSED)
13030       CheckExitSP(x, y);
13031     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13032              element == EL_EXPANDABLE_STEELWALL_GROWING)
13033       MauerWaechst(x, y);
13034     else if (element == EL_EXPANDABLE_WALL ||
13035              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13036              element == EL_EXPANDABLE_WALL_VERTICAL ||
13037              element == EL_EXPANDABLE_WALL_ANY ||
13038              element == EL_BD_EXPANDABLE_WALL)
13039       MauerAbleger(x, y);
13040     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13041              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13042              element == EL_EXPANDABLE_STEELWALL_ANY)
13043       MauerAblegerStahl(x, y);
13044     else if (element == EL_FLAMES)
13045       CheckForDragon(x, y);
13046     else if (element == EL_EXPLOSION)
13047       ; /* drawing of correct explosion animation is handled separately */
13048     else if (element == EL_ELEMENT_SNAPPING ||
13049              element == EL_DIAGONAL_SHRINKING ||
13050              element == EL_DIAGONAL_GROWING)
13051     {
13052       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13053
13054       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13055     }
13056     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13057       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13058
13059 #endif  // ---------------------------------------------------------------------
13060
13061     if (IS_BELT_ACTIVE(element))
13062       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13063
13064     if (game.magic_wall_active)
13065     {
13066       int jx = local_player->jx, jy = local_player->jy;
13067
13068       /* play the element sound at the position nearest to the player */
13069       if ((element == EL_MAGIC_WALL_FULL ||
13070            element == EL_MAGIC_WALL_ACTIVE ||
13071            element == EL_MAGIC_WALL_EMPTYING ||
13072            element == EL_BD_MAGIC_WALL_FULL ||
13073            element == EL_BD_MAGIC_WALL_ACTIVE ||
13074            element == EL_BD_MAGIC_WALL_EMPTYING ||
13075            element == EL_DC_MAGIC_WALL_FULL ||
13076            element == EL_DC_MAGIC_WALL_ACTIVE ||
13077            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13078           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13079       {
13080         magic_wall_x = x;
13081         magic_wall_y = y;
13082       }
13083     }
13084   }
13085
13086 #if 0
13087   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13088 #endif
13089
13090 #if USE_NEW_AMOEBA_CODE
13091   /* new experimental amoeba growth stuff */
13092   if (!(FrameCounter % 8))
13093   {
13094     static unsigned long random = 1684108901;
13095
13096     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13097     {
13098       x = RND(lev_fieldx);
13099       y = RND(lev_fieldy);
13100       element = Feld[x][y];
13101
13102       if (!IS_PLAYER(x,y) &&
13103           (element == EL_EMPTY ||
13104            CAN_GROW_INTO(element) ||
13105            element == EL_QUICKSAND_EMPTY ||
13106            element == EL_QUICKSAND_FAST_EMPTY ||
13107            element == EL_ACID_SPLASH_LEFT ||
13108            element == EL_ACID_SPLASH_RIGHT))
13109       {
13110         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13111             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13112             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13113             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13114           Feld[x][y] = EL_AMOEBA_DROP;
13115       }
13116
13117       random = random * 129 + 1;
13118     }
13119   }
13120 #endif
13121
13122 #if 0
13123   if (game.explosions_delayed)
13124 #endif
13125   {
13126     game.explosions_delayed = FALSE;
13127
13128     SCAN_PLAYFIELD(x, y)
13129     {
13130       element = Feld[x][y];
13131
13132       if (ExplodeField[x][y])
13133         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13134       else if (element == EL_EXPLOSION)
13135         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13136
13137       ExplodeField[x][y] = EX_TYPE_NONE;
13138     }
13139
13140     game.explosions_delayed = TRUE;
13141   }
13142
13143   if (game.magic_wall_active)
13144   {
13145     if (!(game.magic_wall_time_left % 4))
13146     {
13147       int element = Feld[magic_wall_x][magic_wall_y];
13148
13149       if (element == EL_BD_MAGIC_WALL_FULL ||
13150           element == EL_BD_MAGIC_WALL_ACTIVE ||
13151           element == EL_BD_MAGIC_WALL_EMPTYING)
13152         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13153       else if (element == EL_DC_MAGIC_WALL_FULL ||
13154                element == EL_DC_MAGIC_WALL_ACTIVE ||
13155                element == EL_DC_MAGIC_WALL_EMPTYING)
13156         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13157       else
13158         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13159     }
13160
13161     if (game.magic_wall_time_left > 0)
13162     {
13163       game.magic_wall_time_left--;
13164
13165       if (!game.magic_wall_time_left)
13166       {
13167         SCAN_PLAYFIELD(x, y)
13168         {
13169           element = Feld[x][y];
13170
13171           if (element == EL_MAGIC_WALL_ACTIVE ||
13172               element == EL_MAGIC_WALL_FULL)
13173           {
13174             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13175             TEST_DrawLevelField(x, y);
13176           }
13177           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13178                    element == EL_BD_MAGIC_WALL_FULL)
13179           {
13180             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13181             TEST_DrawLevelField(x, y);
13182           }
13183           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13184                    element == EL_DC_MAGIC_WALL_FULL)
13185           {
13186             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13187             TEST_DrawLevelField(x, y);
13188           }
13189         }
13190
13191         game.magic_wall_active = FALSE;
13192       }
13193     }
13194   }
13195
13196   if (game.light_time_left > 0)
13197   {
13198     game.light_time_left--;
13199
13200     if (game.light_time_left == 0)
13201       RedrawAllLightSwitchesAndInvisibleElements();
13202   }
13203
13204   if (game.timegate_time_left > 0)
13205   {
13206     game.timegate_time_left--;
13207
13208     if (game.timegate_time_left == 0)
13209       CloseAllOpenTimegates();
13210   }
13211
13212   if (game.lenses_time_left > 0)
13213   {
13214     game.lenses_time_left--;
13215
13216     if (game.lenses_time_left == 0)
13217       RedrawAllInvisibleElementsForLenses();
13218   }
13219
13220   if (game.magnify_time_left > 0)
13221   {
13222     game.magnify_time_left--;
13223
13224     if (game.magnify_time_left == 0)
13225       RedrawAllInvisibleElementsForMagnifier();
13226   }
13227
13228   for (i = 0; i < MAX_PLAYERS; i++)
13229   {
13230     struct PlayerInfo *player = &stored_player[i];
13231
13232     if (SHIELD_ON(player))
13233     {
13234       if (player->shield_deadly_time_left)
13235         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13236       else if (player->shield_normal_time_left)
13237         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13238     }
13239   }
13240
13241 #if USE_DELAYED_GFX_REDRAW
13242   SCAN_PLAYFIELD(x, y)
13243   {
13244 #if 1
13245     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13246 #else
13247     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13248         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13249 #endif
13250     {
13251       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13252          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13253
13254       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13255         DrawLevelField(x, y);
13256
13257       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13258         DrawLevelFieldCrumbled(x, y);
13259
13260       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13261         DrawLevelFieldCrumbledNeighbours(x, y);
13262
13263       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13264         DrawTwinkleOnField(x, y);
13265     }
13266
13267     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13268   }
13269 #endif
13270
13271   CheckLevelTime();
13272
13273   DrawAllPlayers();
13274   PlayAllPlayersSound();
13275
13276   if (options.debug)                    /* calculate frames per second */
13277   {
13278     static unsigned long fps_counter = 0;
13279     static int fps_frames = 0;
13280     unsigned long fps_delay_ms = Counter() - fps_counter;
13281
13282     fps_frames++;
13283
13284     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13285     {
13286       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13287
13288       fps_frames = 0;
13289       fps_counter = Counter();
13290     }
13291
13292     redraw_mask |= REDRAW_FPS;
13293   }
13294
13295   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13296
13297   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13298   {
13299     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13300
13301     local_player->show_envelope = 0;
13302   }
13303
13304 #if 0
13305   debug_print_timestamp(0, "stop main loop profiling ");
13306   printf("----------------------------------------------------------\n");
13307 #endif
13308
13309   /* use random number generator in every frame to make it less predictable */
13310   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13311     RND(1);
13312 }
13313
13314 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13315 {
13316   int min_x = x, min_y = y, max_x = x, max_y = y;
13317   int i;
13318
13319   for (i = 0; i < MAX_PLAYERS; i++)
13320   {
13321     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13322
13323     if (!stored_player[i].active || &stored_player[i] == player)
13324       continue;
13325
13326     min_x = MIN(min_x, jx);
13327     min_y = MIN(min_y, jy);
13328     max_x = MAX(max_x, jx);
13329     max_y = MAX(max_y, jy);
13330   }
13331
13332   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13333 }
13334
13335 static boolean AllPlayersInVisibleScreen()
13336 {
13337   int i;
13338
13339   for (i = 0; i < MAX_PLAYERS; i++)
13340   {
13341     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13342
13343     if (!stored_player[i].active)
13344       continue;
13345
13346     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13347       return FALSE;
13348   }
13349
13350   return TRUE;
13351 }
13352
13353 void ScrollLevel(int dx, int dy)
13354 {
13355 #if 0
13356   /* (directly solved in BlitBitmap() now) */
13357   static Bitmap *bitmap_db_field2 = NULL;
13358   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13359   int x, y;
13360 #else
13361   int x, y;
13362 #endif
13363
13364 #if 0
13365   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13366   /* only horizontal XOR vertical scroll direction allowed */
13367   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13368     return;
13369 #endif
13370
13371 #if 0
13372   /* (directly solved in BlitBitmap() now) */
13373   if (bitmap_db_field2 == NULL)
13374     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13375
13376   /* needed when blitting directly to same bitmap -- should not be needed with
13377      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13378   BlitBitmap(drawto_field, bitmap_db_field2,
13379              FX + TILEX * (dx == -1) - softscroll_offset,
13380              FY + TILEY * (dy == -1) - softscroll_offset,
13381              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13382              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13383              FX + TILEX * (dx == 1) - softscroll_offset,
13384              FY + TILEY * (dy == 1) - softscroll_offset);
13385   BlitBitmap(bitmap_db_field2, drawto_field,
13386              FX + TILEX * (dx == 1) - softscroll_offset,
13387              FY + TILEY * (dy == 1) - softscroll_offset,
13388              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13389              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13390              FX + TILEX * (dx == 1) - softscroll_offset,
13391              FY + TILEY * (dy == 1) - softscroll_offset);
13392
13393 #else
13394
13395 #if 0
13396   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13397   int xsize = (BX2 - BX1 + 1);
13398   int ysize = (BY2 - BY1 + 1);
13399   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13400   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13401   int step  = (start < end ? +1 : -1);
13402
13403   for (i = start; i != end; i += step)
13404   {
13405     BlitBitmap(drawto_field, drawto_field,
13406                FX + TILEX * (dx != 0 ? i + step : 0),
13407                FY + TILEY * (dy != 0 ? i + step : 0),
13408                TILEX * (dx != 0 ? 1 : xsize),
13409                TILEY * (dy != 0 ? 1 : ysize),
13410                FX + TILEX * (dx != 0 ? i : 0),
13411                FY + TILEY * (dy != 0 ? i : 0));
13412   }
13413
13414 #else
13415
13416 #if NEW_TILESIZE
13417 #if NEW_SCROLL
13418   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13419 #else
13420   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13421 #endif
13422 #else
13423 #if NEW_SCROLL
13424   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13425 #else
13426   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13427 #endif
13428 #endif
13429
13430 #if NEW_TILESIZE
13431   BlitBitmap(drawto_field, drawto_field,
13432              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13433              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13434              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13435              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13436              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13437              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13438 #else
13439   BlitBitmap(drawto_field, drawto_field,
13440              FX + TILEX * (dx == -1) - softscroll_offset,
13441              FY + TILEY * (dy == -1) - softscroll_offset,
13442              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13443              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13444              FX + TILEX * (dx == 1) - softscroll_offset,
13445              FY + TILEY * (dy == 1) - softscroll_offset);
13446 #endif
13447
13448 #endif
13449 #endif
13450
13451   if (dx != 0)
13452   {
13453     x = (dx == 1 ? BX1 : BX2);
13454     for (y = BY1; y <= BY2; y++)
13455       DrawScreenField(x, y);
13456   }
13457
13458   if (dy != 0)
13459   {
13460     y = (dy == 1 ? BY1 : BY2);
13461     for (x = BX1; x <= BX2; x++)
13462       DrawScreenField(x, y);
13463   }
13464
13465   redraw_mask |= REDRAW_FIELD;
13466 }
13467
13468 static boolean canFallDown(struct PlayerInfo *player)
13469 {
13470   int jx = player->jx, jy = player->jy;
13471
13472   return (IN_LEV_FIELD(jx, jy + 1) &&
13473           (IS_FREE(jx, jy + 1) ||
13474            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13475           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13476           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13477 }
13478
13479 static boolean canPassField(int x, int y, int move_dir)
13480 {
13481   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13482   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13483   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13484   int nextx = x + dx;
13485   int nexty = y + dy;
13486   int element = Feld[x][y];
13487
13488   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13489           !CAN_MOVE(element) &&
13490           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13491           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13492           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13493 }
13494
13495 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13496 {
13497   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13498   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13499   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13500   int newx = x + dx;
13501   int newy = y + dy;
13502
13503   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13504           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13505           (IS_DIGGABLE(Feld[newx][newy]) ||
13506            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13507            canPassField(newx, newy, move_dir)));
13508 }
13509
13510 static void CheckGravityMovement(struct PlayerInfo *player)
13511 {
13512 #if USE_PLAYER_GRAVITY
13513   if (player->gravity && !player->programmed_action)
13514 #else
13515   if (game.gravity && !player->programmed_action)
13516 #endif
13517   {
13518     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13519     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13520     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13521     int jx = player->jx, jy = player->jy;
13522     boolean player_is_moving_to_valid_field =
13523       (!player_is_snapping &&
13524        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13525         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13526     boolean player_can_fall_down = canFallDown(player);
13527
13528     if (player_can_fall_down &&
13529         !player_is_moving_to_valid_field)
13530       player->programmed_action = MV_DOWN;
13531   }
13532 }
13533
13534 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13535 {
13536   return CheckGravityMovement(player);
13537
13538 #if USE_PLAYER_GRAVITY
13539   if (player->gravity && !player->programmed_action)
13540 #else
13541   if (game.gravity && !player->programmed_action)
13542 #endif
13543   {
13544     int jx = player->jx, jy = player->jy;
13545     boolean field_under_player_is_free =
13546       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13547     boolean player_is_standing_on_valid_field =
13548       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13549        (IS_WALKABLE(Feld[jx][jy]) &&
13550         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13551
13552     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13553       player->programmed_action = MV_DOWN;
13554   }
13555 }
13556
13557 /*
13558   MovePlayerOneStep()
13559   -----------------------------------------------------------------------------
13560   dx, dy:               direction (non-diagonal) to try to move the player to
13561   real_dx, real_dy:     direction as read from input device (can be diagonal)
13562 */
13563
13564 boolean MovePlayerOneStep(struct PlayerInfo *player,
13565                           int dx, int dy, int real_dx, int real_dy)
13566 {
13567   int jx = player->jx, jy = player->jy;
13568   int new_jx = jx + dx, new_jy = jy + dy;
13569 #if !USE_FIXED_DONT_RUN_INTO
13570   int element;
13571 #endif
13572   int can_move;
13573   boolean player_can_move = !player->cannot_move;
13574
13575   if (!player->active || (!dx && !dy))
13576     return MP_NO_ACTION;
13577
13578   player->MovDir = (dx < 0 ? MV_LEFT :
13579                     dx > 0 ? MV_RIGHT :
13580                     dy < 0 ? MV_UP :
13581                     dy > 0 ? MV_DOWN :  MV_NONE);
13582
13583   if (!IN_LEV_FIELD(new_jx, new_jy))
13584     return MP_NO_ACTION;
13585
13586   if (!player_can_move)
13587   {
13588     if (player->MovPos == 0)
13589     {
13590       player->is_moving = FALSE;
13591       player->is_digging = FALSE;
13592       player->is_collecting = FALSE;
13593       player->is_snapping = FALSE;
13594       player->is_pushing = FALSE;
13595     }
13596   }
13597
13598 #if 1
13599   if (!options.network && game.centered_player_nr == -1 &&
13600       !AllPlayersInSight(player, new_jx, new_jy))
13601     return MP_NO_ACTION;
13602 #else
13603   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13604     return MP_NO_ACTION;
13605 #endif
13606
13607 #if !USE_FIXED_DONT_RUN_INTO
13608   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13609
13610   /* (moved to DigField()) */
13611   if (player_can_move && DONT_RUN_INTO(element))
13612   {
13613     if (element == EL_ACID && dx == 0 && dy == 1)
13614     {
13615       SplashAcid(new_jx, new_jy);
13616       Feld[jx][jy] = EL_PLAYER_1;
13617       InitMovingField(jx, jy, MV_DOWN);
13618       Store[jx][jy] = EL_ACID;
13619       ContinueMoving(jx, jy);
13620       BuryPlayer(player);
13621     }
13622     else
13623       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13624
13625     return MP_MOVING;
13626   }
13627 #endif
13628
13629   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13630   if (can_move != MP_MOVING)
13631     return can_move;
13632
13633   /* check if DigField() has caused relocation of the player */
13634   if (player->jx != jx || player->jy != jy)
13635     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13636
13637   StorePlayer[jx][jy] = 0;
13638   player->last_jx = jx;
13639   player->last_jy = jy;
13640   player->jx = new_jx;
13641   player->jy = new_jy;
13642   StorePlayer[new_jx][new_jy] = player->element_nr;
13643
13644   if (player->move_delay_value_next != -1)
13645   {
13646     player->move_delay_value = player->move_delay_value_next;
13647     player->move_delay_value_next = -1;
13648   }
13649
13650   player->MovPos =
13651     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13652
13653   player->step_counter++;
13654
13655   PlayerVisit[jx][jy] = FrameCounter;
13656
13657 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13658   player->is_moving = TRUE;
13659 #endif
13660
13661 #if 1
13662   /* should better be called in MovePlayer(), but this breaks some tapes */
13663   ScrollPlayer(player, SCROLL_INIT);
13664 #endif
13665
13666   return MP_MOVING;
13667 }
13668
13669 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13670 {
13671   int jx = player->jx, jy = player->jy;
13672   int old_jx = jx, old_jy = jy;
13673   int moved = MP_NO_ACTION;
13674
13675   if (!player->active)
13676     return FALSE;
13677
13678   if (!dx && !dy)
13679   {
13680     if (player->MovPos == 0)
13681     {
13682       player->is_moving = FALSE;
13683       player->is_digging = FALSE;
13684       player->is_collecting = FALSE;
13685       player->is_snapping = FALSE;
13686       player->is_pushing = FALSE;
13687     }
13688
13689     return FALSE;
13690   }
13691
13692   if (player->move_delay > 0)
13693     return FALSE;
13694
13695   player->move_delay = -1;              /* set to "uninitialized" value */
13696
13697   /* store if player is automatically moved to next field */
13698   player->is_auto_moving = (player->programmed_action != MV_NONE);
13699
13700   /* remove the last programmed player action */
13701   player->programmed_action = 0;
13702
13703   if (player->MovPos)
13704   {
13705     /* should only happen if pre-1.2 tape recordings are played */
13706     /* this is only for backward compatibility */
13707
13708     int original_move_delay_value = player->move_delay_value;
13709
13710 #if DEBUG
13711     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13712            tape.counter);
13713 #endif
13714
13715     /* scroll remaining steps with finest movement resolution */
13716     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13717
13718     while (player->MovPos)
13719     {
13720       ScrollPlayer(player, SCROLL_GO_ON);
13721       ScrollScreen(NULL, SCROLL_GO_ON);
13722
13723       AdvanceFrameAndPlayerCounters(player->index_nr);
13724
13725       DrawAllPlayers();
13726       BackToFront();
13727     }
13728
13729     player->move_delay_value = original_move_delay_value;
13730   }
13731
13732   player->is_active = FALSE;
13733
13734   if (player->last_move_dir & MV_HORIZONTAL)
13735   {
13736     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13737       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13738   }
13739   else
13740   {
13741     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13742       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13743   }
13744
13745 #if USE_FIXED_BORDER_RUNNING_GFX
13746   if (!moved && !player->is_active)
13747   {
13748     player->is_moving = FALSE;
13749     player->is_digging = FALSE;
13750     player->is_collecting = FALSE;
13751     player->is_snapping = FALSE;
13752     player->is_pushing = FALSE;
13753   }
13754 #endif
13755
13756   jx = player->jx;
13757   jy = player->jy;
13758
13759 #if 1
13760   if (moved & MP_MOVING && !ScreenMovPos &&
13761       (player->index_nr == game.centered_player_nr ||
13762        game.centered_player_nr == -1))
13763 #else
13764   if (moved & MP_MOVING && !ScreenMovPos &&
13765       (player == local_player || !options.network))
13766 #endif
13767   {
13768     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13769     int offset = game.scroll_delay_value;
13770
13771     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13772     {
13773       /* actual player has left the screen -- scroll in that direction */
13774       if (jx != old_jx)         /* player has moved horizontally */
13775         scroll_x += (jx - old_jx);
13776       else                      /* player has moved vertically */
13777         scroll_y += (jy - old_jy);
13778     }
13779     else
13780     {
13781       if (jx != old_jx)         /* player has moved horizontally */
13782       {
13783         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13784             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13785           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13786
13787         /* don't scroll over playfield boundaries */
13788         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13789           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13790
13791         /* don't scroll more than one field at a time */
13792         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13793
13794         /* don't scroll against the player's moving direction */
13795         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13796             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13797           scroll_x = old_scroll_x;
13798       }
13799       else                      /* player has moved vertically */
13800       {
13801         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13802             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13803           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13804
13805         /* don't scroll over playfield boundaries */
13806         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13807           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13808
13809         /* don't scroll more than one field at a time */
13810         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13811
13812         /* don't scroll against the player's moving direction */
13813         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13814             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13815           scroll_y = old_scroll_y;
13816       }
13817     }
13818
13819     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13820     {
13821 #if 1
13822       if (!options.network && game.centered_player_nr == -1 &&
13823           !AllPlayersInVisibleScreen())
13824       {
13825         scroll_x = old_scroll_x;
13826         scroll_y = old_scroll_y;
13827       }
13828       else
13829 #else
13830       if (!options.network && !AllPlayersInVisibleScreen())
13831       {
13832         scroll_x = old_scroll_x;
13833         scroll_y = old_scroll_y;
13834       }
13835       else
13836 #endif
13837       {
13838         ScrollScreen(player, SCROLL_INIT);
13839         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13840       }
13841     }
13842   }
13843
13844   player->StepFrame = 0;
13845
13846   if (moved & MP_MOVING)
13847   {
13848     if (old_jx != jx && old_jy == jy)
13849       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13850     else if (old_jx == jx && old_jy != jy)
13851       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13852
13853     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13854
13855     player->last_move_dir = player->MovDir;
13856     player->is_moving = TRUE;
13857     player->is_snapping = FALSE;
13858     player->is_switching = FALSE;
13859     player->is_dropping = FALSE;
13860     player->is_dropping_pressed = FALSE;
13861     player->drop_pressed_delay = 0;
13862
13863 #if 0
13864     /* should better be called here than above, but this breaks some tapes */
13865     ScrollPlayer(player, SCROLL_INIT);
13866 #endif
13867   }
13868   else
13869   {
13870     CheckGravityMovementWhenNotMoving(player);
13871
13872     player->is_moving = FALSE;
13873
13874     /* at this point, the player is allowed to move, but cannot move right now
13875        (e.g. because of something blocking the way) -- ensure that the player
13876        is also allowed to move in the next frame (in old versions before 3.1.1,
13877        the player was forced to wait again for eight frames before next try) */
13878
13879     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13880       player->move_delay = 0;   /* allow direct movement in the next frame */
13881   }
13882
13883   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13884     player->move_delay = player->move_delay_value;
13885
13886   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13887   {
13888     TestIfPlayerTouchesBadThing(jx, jy);
13889     TestIfPlayerTouchesCustomElement(jx, jy);
13890   }
13891
13892   if (!player->active)
13893     RemovePlayer(player);
13894
13895   return moved;
13896 }
13897
13898 void ScrollPlayer(struct PlayerInfo *player, int mode)
13899 {
13900   int jx = player->jx, jy = player->jy;
13901   int last_jx = player->last_jx, last_jy = player->last_jy;
13902   int move_stepsize = TILEX / player->move_delay_value;
13903
13904 #if USE_NEW_PLAYER_SPEED
13905   if (!player->active)
13906     return;
13907
13908   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13909     return;
13910 #else
13911   if (!player->active || player->MovPos == 0)
13912     return;
13913 #endif
13914
13915   if (mode == SCROLL_INIT)
13916   {
13917     player->actual_frame_counter = FrameCounter;
13918     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13919
13920     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13921         Feld[last_jx][last_jy] == EL_EMPTY)
13922     {
13923       int last_field_block_delay = 0;   /* start with no blocking at all */
13924       int block_delay_adjustment = player->block_delay_adjustment;
13925
13926       /* if player blocks last field, add delay for exactly one move */
13927       if (player->block_last_field)
13928       {
13929         last_field_block_delay += player->move_delay_value;
13930
13931         /* when blocking enabled, prevent moving up despite gravity */
13932 #if USE_PLAYER_GRAVITY
13933         if (player->gravity && player->MovDir == MV_UP)
13934           block_delay_adjustment = -1;
13935 #else
13936         if (game.gravity && player->MovDir == MV_UP)
13937           block_delay_adjustment = -1;
13938 #endif
13939       }
13940
13941       /* add block delay adjustment (also possible when not blocking) */
13942       last_field_block_delay += block_delay_adjustment;
13943
13944       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13945       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13946     }
13947
13948 #if USE_NEW_PLAYER_SPEED
13949     if (player->MovPos != 0)    /* player has not yet reached destination */
13950       return;
13951 #else
13952     return;
13953 #endif
13954   }
13955   else if (!FrameReached(&player->actual_frame_counter, 1))
13956     return;
13957
13958 #if USE_NEW_PLAYER_SPEED
13959   if (player->MovPos != 0)
13960   {
13961     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13962     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13963
13964     /* before DrawPlayer() to draw correct player graphic for this case */
13965     if (player->MovPos == 0)
13966       CheckGravityMovement(player);
13967   }
13968 #else
13969   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13970   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13971
13972   /* before DrawPlayer() to draw correct player graphic for this case */
13973   if (player->MovPos == 0)
13974     CheckGravityMovement(player);
13975 #endif
13976
13977   if (player->MovPos == 0)      /* player reached destination field */
13978   {
13979     if (player->move_delay_reset_counter > 0)
13980     {
13981       player->move_delay_reset_counter--;
13982
13983       if (player->move_delay_reset_counter == 0)
13984       {
13985         /* continue with normal speed after quickly moving through gate */
13986         HALVE_PLAYER_SPEED(player);
13987
13988         /* be able to make the next move without delay */
13989         player->move_delay = 0;
13990       }
13991     }
13992
13993     player->last_jx = jx;
13994     player->last_jy = jy;
13995
13996     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13997         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13998 #if 1
13999         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14000 #endif
14001         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14002         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14003 #if 1
14004         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14005 #endif
14006         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14007         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14008     {
14009       DrawPlayer(player);       /* needed here only to cleanup last field */
14010       RemovePlayer(player);
14011
14012       if (local_player->friends_still_needed == 0 ||
14013           IS_SP_ELEMENT(Feld[jx][jy]))
14014         PlayerWins(player);
14015     }
14016
14017     /* this breaks one level: "machine", level 000 */
14018     {
14019       int move_direction = player->MovDir;
14020       int enter_side = MV_DIR_OPPOSITE(move_direction);
14021       int leave_side = move_direction;
14022       int old_jx = last_jx;
14023       int old_jy = last_jy;
14024       int old_element = Feld[old_jx][old_jy];
14025       int new_element = Feld[jx][jy];
14026
14027       if (IS_CUSTOM_ELEMENT(old_element))
14028         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14029                                    CE_LEFT_BY_PLAYER,
14030                                    player->index_bit, leave_side);
14031
14032       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14033                                           CE_PLAYER_LEAVES_X,
14034                                           player->index_bit, leave_side);
14035
14036       if (IS_CUSTOM_ELEMENT(new_element))
14037         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14038                                    player->index_bit, enter_side);
14039
14040       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14041                                           CE_PLAYER_ENTERS_X,
14042                                           player->index_bit, enter_side);
14043
14044 #if USE_FIX_CE_ACTION_WITH_PLAYER
14045       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14046                                         CE_MOVE_OF_X, move_direction);
14047 #else
14048       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14049                                         CE_MOVE_OF_X, move_direction);
14050 #endif
14051     }
14052
14053     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14054     {
14055       TestIfPlayerTouchesBadThing(jx, jy);
14056       TestIfPlayerTouchesCustomElement(jx, jy);
14057
14058       /* needed because pushed element has not yet reached its destination,
14059          so it would trigger a change event at its previous field location */
14060       if (!player->is_pushing)
14061         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14062
14063       if (!player->active)
14064         RemovePlayer(player);
14065     }
14066
14067     if (!local_player->LevelSolved && level.use_step_counter)
14068     {
14069       int i;
14070
14071       TimePlayed++;
14072
14073       if (TimeLeft > 0)
14074       {
14075         TimeLeft--;
14076
14077         if (TimeLeft <= 10 && setup.time_limit)
14078           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14079
14080 #if 1
14081         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14082
14083         DisplayGameControlValues();
14084 #else
14085         DrawGameValue_Time(TimeLeft);
14086 #endif
14087
14088         if (!TimeLeft && setup.time_limit)
14089           for (i = 0; i < MAX_PLAYERS; i++)
14090             KillPlayer(&stored_player[i]);
14091       }
14092 #if 1
14093       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14094       {
14095         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14096
14097         DisplayGameControlValues();
14098       }
14099 #else
14100       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14101         DrawGameValue_Time(TimePlayed);
14102 #endif
14103     }
14104
14105     if (tape.single_step && tape.recording && !tape.pausing &&
14106         !player->programmed_action)
14107       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14108   }
14109 }
14110
14111 void ScrollScreen(struct PlayerInfo *player, int mode)
14112 {
14113   static unsigned long screen_frame_counter = 0;
14114
14115   if (mode == SCROLL_INIT)
14116   {
14117     /* set scrolling step size according to actual player's moving speed */
14118     ScrollStepSize = TILEX / player->move_delay_value;
14119
14120     screen_frame_counter = FrameCounter;
14121     ScreenMovDir = player->MovDir;
14122     ScreenMovPos = player->MovPos;
14123     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14124     return;
14125   }
14126   else if (!FrameReached(&screen_frame_counter, 1))
14127     return;
14128
14129   if (ScreenMovPos)
14130   {
14131     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14132     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14133     redraw_mask |= REDRAW_FIELD;
14134   }
14135   else
14136     ScreenMovDir = MV_NONE;
14137 }
14138
14139 void TestIfPlayerTouchesCustomElement(int x, int y)
14140 {
14141   static int xy[4][2] =
14142   {
14143     { 0, -1 },
14144     { -1, 0 },
14145     { +1, 0 },
14146     { 0, +1 }
14147   };
14148   static int trigger_sides[4][2] =
14149   {
14150     /* center side       border side */
14151     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14152     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14153     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14154     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14155   };
14156   static int touch_dir[4] =
14157   {
14158     MV_LEFT | MV_RIGHT,
14159     MV_UP   | MV_DOWN,
14160     MV_UP   | MV_DOWN,
14161     MV_LEFT | MV_RIGHT
14162   };
14163   int center_element = Feld[x][y];      /* should always be non-moving! */
14164   int i;
14165
14166   for (i = 0; i < NUM_DIRECTIONS; i++)
14167   {
14168     int xx = x + xy[i][0];
14169     int yy = y + xy[i][1];
14170     int center_side = trigger_sides[i][0];
14171     int border_side = trigger_sides[i][1];
14172     int border_element;
14173
14174     if (!IN_LEV_FIELD(xx, yy))
14175       continue;
14176
14177     if (IS_PLAYER(x, y))                /* player found at center element */
14178     {
14179       struct PlayerInfo *player = PLAYERINFO(x, y);
14180
14181       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14182         border_element = Feld[xx][yy];          /* may be moving! */
14183       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14184         border_element = Feld[xx][yy];
14185       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14186         border_element = MovingOrBlocked2Element(xx, yy);
14187       else
14188         continue;               /* center and border element do not touch */
14189
14190       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14191                                  player->index_bit, border_side);
14192       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14193                                           CE_PLAYER_TOUCHES_X,
14194                                           player->index_bit, border_side);
14195
14196 #if USE_FIX_CE_ACTION_WITH_PLAYER
14197       {
14198         /* use player element that is initially defined in the level playfield,
14199            not the player element that corresponds to the runtime player number
14200            (example: a level that contains EL_PLAYER_3 as the only player would
14201            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14202         int player_element = PLAYERINFO(x, y)->initial_element;
14203
14204         CheckElementChangeBySide(xx, yy, border_element, player_element,
14205                                  CE_TOUCHING_X, border_side);
14206       }
14207 #endif
14208     }
14209     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14210     {
14211       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14212
14213       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14214       {
14215         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14216           continue;             /* center and border element do not touch */
14217       }
14218
14219       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14220                                  player->index_bit, center_side);
14221       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14222                                           CE_PLAYER_TOUCHES_X,
14223                                           player->index_bit, center_side);
14224
14225 #if USE_FIX_CE_ACTION_WITH_PLAYER
14226       {
14227         /* use player element that is initially defined in the level playfield,
14228            not the player element that corresponds to the runtime player number
14229            (example: a level that contains EL_PLAYER_3 as the only player would
14230            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14231         int player_element = PLAYERINFO(xx, yy)->initial_element;
14232
14233         CheckElementChangeBySide(x, y, center_element, player_element,
14234                                  CE_TOUCHING_X, center_side);
14235       }
14236 #endif
14237
14238       break;
14239     }
14240   }
14241 }
14242
14243 #if USE_ELEMENT_TOUCHING_BUGFIX
14244
14245 void TestIfElementTouchesCustomElement(int x, int y)
14246 {
14247   static int xy[4][2] =
14248   {
14249     { 0, -1 },
14250     { -1, 0 },
14251     { +1, 0 },
14252     { 0, +1 }
14253   };
14254   static int trigger_sides[4][2] =
14255   {
14256     /* center side      border side */
14257     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14258     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14259     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14260     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14261   };
14262   static int touch_dir[4] =
14263   {
14264     MV_LEFT | MV_RIGHT,
14265     MV_UP   | MV_DOWN,
14266     MV_UP   | MV_DOWN,
14267     MV_LEFT | MV_RIGHT
14268   };
14269   boolean change_center_element = FALSE;
14270   int center_element = Feld[x][y];      /* should always be non-moving! */
14271   int border_element_old[NUM_DIRECTIONS];
14272   int i;
14273
14274   for (i = 0; i < NUM_DIRECTIONS; i++)
14275   {
14276     int xx = x + xy[i][0];
14277     int yy = y + xy[i][1];
14278     int border_element;
14279
14280     border_element_old[i] = -1;
14281
14282     if (!IN_LEV_FIELD(xx, yy))
14283       continue;
14284
14285     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14286       border_element = Feld[xx][yy];    /* may be moving! */
14287     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14288       border_element = Feld[xx][yy];
14289     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14290       border_element = MovingOrBlocked2Element(xx, yy);
14291     else
14292       continue;                 /* center and border element do not touch */
14293
14294     border_element_old[i] = border_element;
14295   }
14296
14297   for (i = 0; i < NUM_DIRECTIONS; i++)
14298   {
14299     int xx = x + xy[i][0];
14300     int yy = y + xy[i][1];
14301     int center_side = trigger_sides[i][0];
14302     int border_element = border_element_old[i];
14303
14304     if (border_element == -1)
14305       continue;
14306
14307     /* check for change of border element */
14308     CheckElementChangeBySide(xx, yy, border_element, center_element,
14309                              CE_TOUCHING_X, center_side);
14310
14311     /* (center element cannot be player, so we dont have to check this here) */
14312   }
14313
14314   for (i = 0; i < NUM_DIRECTIONS; i++)
14315   {
14316     int xx = x + xy[i][0];
14317     int yy = y + xy[i][1];
14318     int border_side = trigger_sides[i][1];
14319     int border_element = border_element_old[i];
14320
14321     if (border_element == -1)
14322       continue;
14323
14324     /* check for change of center element (but change it only once) */
14325     if (!change_center_element)
14326       change_center_element =
14327         CheckElementChangeBySide(x, y, center_element, border_element,
14328                                  CE_TOUCHING_X, border_side);
14329
14330 #if USE_FIX_CE_ACTION_WITH_PLAYER
14331     if (IS_PLAYER(xx, yy))
14332     {
14333       /* use player element that is initially defined in the level playfield,
14334          not the player element that corresponds to the runtime player number
14335          (example: a level that contains EL_PLAYER_3 as the only player would
14336          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14337       int player_element = PLAYERINFO(xx, yy)->initial_element;
14338
14339       CheckElementChangeBySide(x, y, center_element, player_element,
14340                                CE_TOUCHING_X, border_side);
14341     }
14342 #endif
14343   }
14344 }
14345
14346 #else
14347
14348 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14349 {
14350   static int xy[4][2] =
14351   {
14352     { 0, -1 },
14353     { -1, 0 },
14354     { +1, 0 },
14355     { 0, +1 }
14356   };
14357   static int trigger_sides[4][2] =
14358   {
14359     /* center side      border side */
14360     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14361     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14362     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14363     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14364   };
14365   static int touch_dir[4] =
14366   {
14367     MV_LEFT | MV_RIGHT,
14368     MV_UP   | MV_DOWN,
14369     MV_UP   | MV_DOWN,
14370     MV_LEFT | MV_RIGHT
14371   };
14372   boolean change_center_element = FALSE;
14373   int center_element = Feld[x][y];      /* should always be non-moving! */
14374   int i;
14375
14376   for (i = 0; i < NUM_DIRECTIONS; i++)
14377   {
14378     int xx = x + xy[i][0];
14379     int yy = y + xy[i][1];
14380     int center_side = trigger_sides[i][0];
14381     int border_side = trigger_sides[i][1];
14382     int border_element;
14383
14384     if (!IN_LEV_FIELD(xx, yy))
14385       continue;
14386
14387     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14388       border_element = Feld[xx][yy];    /* may be moving! */
14389     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14390       border_element = Feld[xx][yy];
14391     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14392       border_element = MovingOrBlocked2Element(xx, yy);
14393     else
14394       continue;                 /* center and border element do not touch */
14395
14396     /* check for change of center element (but change it only once) */
14397     if (!change_center_element)
14398       change_center_element =
14399         CheckElementChangeBySide(x, y, center_element, border_element,
14400                                  CE_TOUCHING_X, border_side);
14401
14402     /* check for change of border element */
14403     CheckElementChangeBySide(xx, yy, border_element, center_element,
14404                              CE_TOUCHING_X, center_side);
14405   }
14406 }
14407
14408 #endif
14409
14410 void TestIfElementHitsCustomElement(int x, int y, int direction)
14411 {
14412   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14413   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14414   int hitx = x + dx, hity = y + dy;
14415   int hitting_element = Feld[x][y];
14416   int touched_element;
14417
14418   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14419     return;
14420
14421   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14422                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14423
14424   if (IN_LEV_FIELD(hitx, hity))
14425   {
14426     int opposite_direction = MV_DIR_OPPOSITE(direction);
14427     int hitting_side = direction;
14428     int touched_side = opposite_direction;
14429     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14430                           MovDir[hitx][hity] != direction ||
14431                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14432
14433     object_hit = TRUE;
14434
14435     if (object_hit)
14436     {
14437       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14438                                CE_HITTING_X, touched_side);
14439
14440       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14441                                CE_HIT_BY_X, hitting_side);
14442
14443       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14444                                CE_HIT_BY_SOMETHING, opposite_direction);
14445
14446 #if USE_FIX_CE_ACTION_WITH_PLAYER
14447       if (IS_PLAYER(hitx, hity))
14448       {
14449         /* use player element that is initially defined in the level playfield,
14450            not the player element that corresponds to the runtime player number
14451            (example: a level that contains EL_PLAYER_3 as the only player would
14452            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14453         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14454
14455         CheckElementChangeBySide(x, y, hitting_element, player_element,
14456                                  CE_HITTING_X, touched_side);
14457       }
14458 #endif
14459     }
14460   }
14461
14462   /* "hitting something" is also true when hitting the playfield border */
14463   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14464                            CE_HITTING_SOMETHING, direction);
14465 }
14466
14467 #if 0
14468 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14469 {
14470   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14471   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14472   int hitx = x + dx, hity = y + dy;
14473   int hitting_element = Feld[x][y];
14474   int touched_element;
14475 #if 0
14476   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14477                         !IS_FREE(hitx, hity) &&
14478                         (!IS_MOVING(hitx, hity) ||
14479                          MovDir[hitx][hity] != direction ||
14480                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14481 #endif
14482
14483   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14484     return;
14485
14486 #if 0
14487   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14488     return;
14489 #endif
14490
14491   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14492                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14493
14494   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14495                            EP_CAN_SMASH_EVERYTHING, direction);
14496
14497   if (IN_LEV_FIELD(hitx, hity))
14498   {
14499     int opposite_direction = MV_DIR_OPPOSITE(direction);
14500     int hitting_side = direction;
14501     int touched_side = opposite_direction;
14502 #if 0
14503     int touched_element = MovingOrBlocked2Element(hitx, hity);
14504 #endif
14505 #if 1
14506     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14507                           MovDir[hitx][hity] != direction ||
14508                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14509
14510     object_hit = TRUE;
14511 #endif
14512
14513     if (object_hit)
14514     {
14515       int i;
14516
14517       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14518                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14519
14520       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14521                                CE_OTHER_IS_SMASHING, touched_side);
14522
14523       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14524                                CE_OTHER_GETS_SMASHED, hitting_side);
14525     }
14526   }
14527 }
14528 #endif
14529
14530 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14531 {
14532   int i, kill_x = -1, kill_y = -1;
14533
14534   int bad_element = -1;
14535   static int test_xy[4][2] =
14536   {
14537     { 0, -1 },
14538     { -1, 0 },
14539     { +1, 0 },
14540     { 0, +1 }
14541   };
14542   static int test_dir[4] =
14543   {
14544     MV_UP,
14545     MV_LEFT,
14546     MV_RIGHT,
14547     MV_DOWN
14548   };
14549
14550   for (i = 0; i < NUM_DIRECTIONS; i++)
14551   {
14552     int test_x, test_y, test_move_dir, test_element;
14553
14554     test_x = good_x + test_xy[i][0];
14555     test_y = good_y + test_xy[i][1];
14556
14557     if (!IN_LEV_FIELD(test_x, test_y))
14558       continue;
14559
14560     test_move_dir =
14561       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14562
14563     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14564
14565     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14566        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14567     */
14568     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14569         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14570     {
14571       kill_x = test_x;
14572       kill_y = test_y;
14573       bad_element = test_element;
14574
14575       break;
14576     }
14577   }
14578
14579   if (kill_x != -1 || kill_y != -1)
14580   {
14581     if (IS_PLAYER(good_x, good_y))
14582     {
14583       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14584
14585       if (player->shield_deadly_time_left > 0 &&
14586           !IS_INDESTRUCTIBLE(bad_element))
14587         Bang(kill_x, kill_y);
14588       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14589         KillPlayer(player);
14590     }
14591     else
14592       Bang(good_x, good_y);
14593   }
14594 }
14595
14596 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14597 {
14598   int i, kill_x = -1, kill_y = -1;
14599   int bad_element = Feld[bad_x][bad_y];
14600   static int test_xy[4][2] =
14601   {
14602     { 0, -1 },
14603     { -1, 0 },
14604     { +1, 0 },
14605     { 0, +1 }
14606   };
14607   static int touch_dir[4] =
14608   {
14609     MV_LEFT | MV_RIGHT,
14610     MV_UP   | MV_DOWN,
14611     MV_UP   | MV_DOWN,
14612     MV_LEFT | MV_RIGHT
14613   };
14614   static int test_dir[4] =
14615   {
14616     MV_UP,
14617     MV_LEFT,
14618     MV_RIGHT,
14619     MV_DOWN
14620   };
14621
14622   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14623     return;
14624
14625   for (i = 0; i < NUM_DIRECTIONS; i++)
14626   {
14627     int test_x, test_y, test_move_dir, test_element;
14628
14629     test_x = bad_x + test_xy[i][0];
14630     test_y = bad_y + test_xy[i][1];
14631
14632     if (!IN_LEV_FIELD(test_x, test_y))
14633       continue;
14634
14635     test_move_dir =
14636       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14637
14638     test_element = Feld[test_x][test_y];
14639
14640     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14641        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14642     */
14643     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14644         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14645     {
14646       /* good thing is player or penguin that does not move away */
14647       if (IS_PLAYER(test_x, test_y))
14648       {
14649         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14650
14651         if (bad_element == EL_ROBOT && player->is_moving)
14652           continue;     /* robot does not kill player if he is moving */
14653
14654         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14655         {
14656           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14657             continue;           /* center and border element do not touch */
14658         }
14659
14660         kill_x = test_x;
14661         kill_y = test_y;
14662
14663         break;
14664       }
14665       else if (test_element == EL_PENGUIN)
14666       {
14667         kill_x = test_x;
14668         kill_y = test_y;
14669
14670         break;
14671       }
14672     }
14673   }
14674
14675   if (kill_x != -1 || kill_y != -1)
14676   {
14677     if (IS_PLAYER(kill_x, kill_y))
14678     {
14679       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14680
14681       if (player->shield_deadly_time_left > 0 &&
14682           !IS_INDESTRUCTIBLE(bad_element))
14683         Bang(bad_x, bad_y);
14684       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14685         KillPlayer(player);
14686     }
14687     else
14688       Bang(kill_x, kill_y);
14689   }
14690 }
14691
14692 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14693 {
14694   int bad_element = Feld[bad_x][bad_y];
14695   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14696   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14697   int test_x = bad_x + dx, test_y = bad_y + dy;
14698   int test_move_dir, test_element;
14699   int kill_x = -1, kill_y = -1;
14700
14701   if (!IN_LEV_FIELD(test_x, test_y))
14702     return;
14703
14704   test_move_dir =
14705     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14706
14707   test_element = Feld[test_x][test_y];
14708
14709   if (test_move_dir != bad_move_dir)
14710   {
14711     /* good thing can be player or penguin that does not move away */
14712     if (IS_PLAYER(test_x, test_y))
14713     {
14714       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14715
14716       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14717          player as being hit when he is moving towards the bad thing, because
14718          the "get hit by" condition would be lost after the player stops) */
14719       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14720         return;         /* player moves away from bad thing */
14721
14722       kill_x = test_x;
14723       kill_y = test_y;
14724     }
14725     else if (test_element == EL_PENGUIN)
14726     {
14727       kill_x = test_x;
14728       kill_y = test_y;
14729     }
14730   }
14731
14732   if (kill_x != -1 || kill_y != -1)
14733   {
14734     if (IS_PLAYER(kill_x, kill_y))
14735     {
14736       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14737
14738       if (player->shield_deadly_time_left > 0 &&
14739           !IS_INDESTRUCTIBLE(bad_element))
14740         Bang(bad_x, bad_y);
14741       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14742         KillPlayer(player);
14743     }
14744     else
14745       Bang(kill_x, kill_y);
14746   }
14747 }
14748
14749 void TestIfPlayerTouchesBadThing(int x, int y)
14750 {
14751   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14752 }
14753
14754 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14755 {
14756   TestIfGoodThingHitsBadThing(x, y, move_dir);
14757 }
14758
14759 void TestIfBadThingTouchesPlayer(int x, int y)
14760 {
14761   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14762 }
14763
14764 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14765 {
14766   TestIfBadThingHitsGoodThing(x, y, move_dir);
14767 }
14768
14769 void TestIfFriendTouchesBadThing(int x, int y)
14770 {
14771   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14772 }
14773
14774 void TestIfBadThingTouchesFriend(int x, int y)
14775 {
14776   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14777 }
14778
14779 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14780 {
14781   int i, kill_x = bad_x, kill_y = bad_y;
14782   static int xy[4][2] =
14783   {
14784     { 0, -1 },
14785     { -1, 0 },
14786     { +1, 0 },
14787     { 0, +1 }
14788   };
14789
14790   for (i = 0; i < NUM_DIRECTIONS; i++)
14791   {
14792     int x, y, element;
14793
14794     x = bad_x + xy[i][0];
14795     y = bad_y + xy[i][1];
14796     if (!IN_LEV_FIELD(x, y))
14797       continue;
14798
14799     element = Feld[x][y];
14800     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14801         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14802     {
14803       kill_x = x;
14804       kill_y = y;
14805       break;
14806     }
14807   }
14808
14809   if (kill_x != bad_x || kill_y != bad_y)
14810     Bang(bad_x, bad_y);
14811 }
14812
14813 void KillPlayer(struct PlayerInfo *player)
14814 {
14815   int jx = player->jx, jy = player->jy;
14816
14817   if (!player->active)
14818     return;
14819
14820 #if 0
14821   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14822          player->killed, player->active, player->reanimated);
14823 #endif
14824
14825   /* the following code was introduced to prevent an infinite loop when calling
14826      -> Bang()
14827      -> CheckTriggeredElementChangeExt()
14828      -> ExecuteCustomElementAction()
14829      -> KillPlayer()
14830      -> (infinitely repeating the above sequence of function calls)
14831      which occurs when killing the player while having a CE with the setting
14832      "kill player X when explosion of <player X>"; the solution using a new
14833      field "player->killed" was chosen for backwards compatibility, although
14834      clever use of the fields "player->active" etc. would probably also work */
14835 #if 1
14836   if (player->killed)
14837     return;
14838 #endif
14839
14840   player->killed = TRUE;
14841
14842   /* remove accessible field at the player's position */
14843   Feld[jx][jy] = EL_EMPTY;
14844
14845   /* deactivate shield (else Bang()/Explode() would not work right) */
14846   player->shield_normal_time_left = 0;
14847   player->shield_deadly_time_left = 0;
14848
14849 #if 0
14850   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14851          player->killed, player->active, player->reanimated);
14852 #endif
14853
14854   Bang(jx, jy);
14855
14856 #if 0
14857   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14858          player->killed, player->active, player->reanimated);
14859 #endif
14860
14861 #if USE_PLAYER_REANIMATION
14862 #if 1
14863   if (player->reanimated)       /* killed player may have been reanimated */
14864     player->killed = player->reanimated = FALSE;
14865   else
14866     BuryPlayer(player);
14867 #else
14868   if (player->killed)           /* player may have been reanimated */
14869     BuryPlayer(player);
14870 #endif
14871 #else
14872   BuryPlayer(player);
14873 #endif
14874 }
14875
14876 static void KillPlayerUnlessEnemyProtected(int x, int y)
14877 {
14878   if (!PLAYER_ENEMY_PROTECTED(x, y))
14879     KillPlayer(PLAYERINFO(x, y));
14880 }
14881
14882 static void KillPlayerUnlessExplosionProtected(int x, int y)
14883 {
14884   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14885     KillPlayer(PLAYERINFO(x, y));
14886 }
14887
14888 void BuryPlayer(struct PlayerInfo *player)
14889 {
14890   int jx = player->jx, jy = player->jy;
14891
14892   if (!player->active)
14893     return;
14894
14895   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14896   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14897
14898   player->GameOver = TRUE;
14899   RemovePlayer(player);
14900 }
14901
14902 void RemovePlayer(struct PlayerInfo *player)
14903 {
14904   int jx = player->jx, jy = player->jy;
14905   int i, found = FALSE;
14906
14907   player->present = FALSE;
14908   player->active = FALSE;
14909
14910   if (!ExplodeField[jx][jy])
14911     StorePlayer[jx][jy] = 0;
14912
14913   if (player->is_moving)
14914     TEST_DrawLevelField(player->last_jx, player->last_jy);
14915
14916   for (i = 0; i < MAX_PLAYERS; i++)
14917     if (stored_player[i].active)
14918       found = TRUE;
14919
14920   if (!found)
14921     AllPlayersGone = TRUE;
14922
14923   ExitX = ZX = jx;
14924   ExitY = ZY = jy;
14925 }
14926
14927 #if USE_NEW_SNAP_DELAY
14928 static void setFieldForSnapping(int x, int y, int element, int direction)
14929 {
14930   struct ElementInfo *ei = &element_info[element];
14931   int direction_bit = MV_DIR_TO_BIT(direction);
14932   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14933   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14934                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14935
14936   Feld[x][y] = EL_ELEMENT_SNAPPING;
14937   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14938
14939   ResetGfxAnimation(x, y);
14940
14941   GfxElement[x][y] = element;
14942   GfxAction[x][y] = action;
14943   GfxDir[x][y] = direction;
14944   GfxFrame[x][y] = -1;
14945 }
14946 #endif
14947
14948 /*
14949   =============================================================================
14950   checkDiagonalPushing()
14951   -----------------------------------------------------------------------------
14952   check if diagonal input device direction results in pushing of object
14953   (by checking if the alternative direction is walkable, diggable, ...)
14954   =============================================================================
14955 */
14956
14957 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14958                                     int x, int y, int real_dx, int real_dy)
14959 {
14960   int jx, jy, dx, dy, xx, yy;
14961
14962   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14963     return TRUE;
14964
14965   /* diagonal direction: check alternative direction */
14966   jx = player->jx;
14967   jy = player->jy;
14968   dx = x - jx;
14969   dy = y - jy;
14970   xx = jx + (dx == 0 ? real_dx : 0);
14971   yy = jy + (dy == 0 ? real_dy : 0);
14972
14973   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14974 }
14975
14976 /*
14977   =============================================================================
14978   DigField()
14979   -----------------------------------------------------------------------------
14980   x, y:                 field next to player (non-diagonal) to try to dig to
14981   real_dx, real_dy:     direction as read from input device (can be diagonal)
14982   =============================================================================
14983 */
14984
14985 static int DigField(struct PlayerInfo *player,
14986                     int oldx, int oldy, int x, int y,
14987                     int real_dx, int real_dy, int mode)
14988 {
14989   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14990   boolean player_was_pushing = player->is_pushing;
14991   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14992   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14993   int jx = oldx, jy = oldy;
14994   int dx = x - jx, dy = y - jy;
14995   int nextx = x + dx, nexty = y + dy;
14996   int move_direction = (dx == -1 ? MV_LEFT  :
14997                         dx == +1 ? MV_RIGHT :
14998                         dy == -1 ? MV_UP    :
14999                         dy == +1 ? MV_DOWN  : MV_NONE);
15000   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15001   int dig_side = MV_DIR_OPPOSITE(move_direction);
15002   int old_element = Feld[jx][jy];
15003 #if USE_FIXED_DONT_RUN_INTO
15004   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15005 #else
15006   int element;
15007 #endif
15008   int collect_count;
15009
15010   if (is_player)                /* function can also be called by EL_PENGUIN */
15011   {
15012     if (player->MovPos == 0)
15013     {
15014       player->is_digging = FALSE;
15015       player->is_collecting = FALSE;
15016     }
15017
15018     if (player->MovPos == 0)    /* last pushing move finished */
15019       player->is_pushing = FALSE;
15020
15021     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15022     {
15023       player->is_switching = FALSE;
15024       player->push_delay = -1;
15025
15026       return MP_NO_ACTION;
15027     }
15028   }
15029
15030 #if !USE_FIXED_DONT_RUN_INTO
15031   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15032     return MP_NO_ACTION;
15033 #endif
15034
15035   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15036     old_element = Back[jx][jy];
15037
15038   /* in case of element dropped at player position, check background */
15039   else if (Back[jx][jy] != EL_EMPTY &&
15040            game.engine_version >= VERSION_IDENT(2,2,0,0))
15041     old_element = Back[jx][jy];
15042
15043   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15044     return MP_NO_ACTION;        /* field has no opening in this direction */
15045
15046   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15047     return MP_NO_ACTION;        /* field has no opening in this direction */
15048
15049 #if USE_FIXED_DONT_RUN_INTO
15050   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15051   {
15052     SplashAcid(x, y);
15053
15054     Feld[jx][jy] = player->artwork_element;
15055     InitMovingField(jx, jy, MV_DOWN);
15056     Store[jx][jy] = EL_ACID;
15057     ContinueMoving(jx, jy);
15058     BuryPlayer(player);
15059
15060     return MP_DONT_RUN_INTO;
15061   }
15062 #endif
15063
15064 #if USE_FIXED_DONT_RUN_INTO
15065   if (player_can_move && DONT_RUN_INTO(element))
15066   {
15067     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15068
15069     return MP_DONT_RUN_INTO;
15070   }
15071 #endif
15072
15073 #if USE_FIXED_DONT_RUN_INTO
15074   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15075     return MP_NO_ACTION;
15076 #endif
15077
15078 #if !USE_FIXED_DONT_RUN_INTO
15079   element = Feld[x][y];
15080 #endif
15081
15082   collect_count = element_info[element].collect_count_initial;
15083
15084   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15085     return MP_NO_ACTION;
15086
15087   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15088     player_can_move = player_can_move_or_snap;
15089
15090   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15091       game.engine_version >= VERSION_IDENT(2,2,0,0))
15092   {
15093     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15094                                player->index_bit, dig_side);
15095     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15096                                         player->index_bit, dig_side);
15097
15098     if (element == EL_DC_LANDMINE)
15099       Bang(x, y);
15100
15101     if (Feld[x][y] != element)          /* field changed by snapping */
15102       return MP_ACTION;
15103
15104     return MP_NO_ACTION;
15105   }
15106
15107 #if USE_PLAYER_GRAVITY
15108   if (player->gravity && is_player && !player->is_auto_moving &&
15109       canFallDown(player) && move_direction != MV_DOWN &&
15110       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15111     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15112 #else
15113   if (game.gravity && is_player && !player->is_auto_moving &&
15114       canFallDown(player) && move_direction != MV_DOWN &&
15115       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15116     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15117 #endif
15118
15119   if (player_can_move &&
15120       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15121   {
15122     int sound_element = SND_ELEMENT(element);
15123     int sound_action = ACTION_WALKING;
15124
15125     if (IS_RND_GATE(element))
15126     {
15127       if (!player->key[RND_GATE_NR(element)])
15128         return MP_NO_ACTION;
15129     }
15130     else if (IS_RND_GATE_GRAY(element))
15131     {
15132       if (!player->key[RND_GATE_GRAY_NR(element)])
15133         return MP_NO_ACTION;
15134     }
15135     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15136     {
15137       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15138         return MP_NO_ACTION;
15139     }
15140     else if (element == EL_EXIT_OPEN ||
15141              element == EL_EM_EXIT_OPEN ||
15142 #if 1
15143              element == EL_EM_EXIT_OPENING ||
15144 #endif
15145              element == EL_STEEL_EXIT_OPEN ||
15146              element == EL_EM_STEEL_EXIT_OPEN ||
15147 #if 1
15148              element == EL_EM_STEEL_EXIT_OPENING ||
15149 #endif
15150              element == EL_SP_EXIT_OPEN ||
15151              element == EL_SP_EXIT_OPENING)
15152     {
15153       sound_action = ACTION_PASSING;    /* player is passing exit */
15154     }
15155     else if (element == EL_EMPTY)
15156     {
15157       sound_action = ACTION_MOVING;             /* nothing to walk on */
15158     }
15159
15160     /* play sound from background or player, whatever is available */
15161     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15162       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15163     else
15164       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15165   }
15166   else if (player_can_move &&
15167            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15168   {
15169     if (!ACCESS_FROM(element, opposite_direction))
15170       return MP_NO_ACTION;      /* field not accessible from this direction */
15171
15172     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15173       return MP_NO_ACTION;
15174
15175     if (IS_EM_GATE(element))
15176     {
15177       if (!player->key[EM_GATE_NR(element)])
15178         return MP_NO_ACTION;
15179     }
15180     else if (IS_EM_GATE_GRAY(element))
15181     {
15182       if (!player->key[EM_GATE_GRAY_NR(element)])
15183         return MP_NO_ACTION;
15184     }
15185     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15186     {
15187       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15188         return MP_NO_ACTION;
15189     }
15190     else if (IS_EMC_GATE(element))
15191     {
15192       if (!player->key[EMC_GATE_NR(element)])
15193         return MP_NO_ACTION;
15194     }
15195     else if (IS_EMC_GATE_GRAY(element))
15196     {
15197       if (!player->key[EMC_GATE_GRAY_NR(element)])
15198         return MP_NO_ACTION;
15199     }
15200     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15201     {
15202       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15203         return MP_NO_ACTION;
15204     }
15205     else if (element == EL_DC_GATE_WHITE ||
15206              element == EL_DC_GATE_WHITE_GRAY ||
15207              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15208     {
15209       if (player->num_white_keys == 0)
15210         return MP_NO_ACTION;
15211
15212       player->num_white_keys--;
15213     }
15214     else if (IS_SP_PORT(element))
15215     {
15216       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15217           element == EL_SP_GRAVITY_PORT_RIGHT ||
15218           element == EL_SP_GRAVITY_PORT_UP ||
15219           element == EL_SP_GRAVITY_PORT_DOWN)
15220 #if USE_PLAYER_GRAVITY
15221         player->gravity = !player->gravity;
15222 #else
15223         game.gravity = !game.gravity;
15224 #endif
15225       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15226                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15227                element == EL_SP_GRAVITY_ON_PORT_UP ||
15228                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15229 #if USE_PLAYER_GRAVITY
15230         player->gravity = TRUE;
15231 #else
15232         game.gravity = TRUE;
15233 #endif
15234       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15235                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15236                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15237                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15238 #if USE_PLAYER_GRAVITY
15239         player->gravity = FALSE;
15240 #else
15241         game.gravity = FALSE;
15242 #endif
15243     }
15244
15245     /* automatically move to the next field with double speed */
15246     player->programmed_action = move_direction;
15247
15248     if (player->move_delay_reset_counter == 0)
15249     {
15250       player->move_delay_reset_counter = 2;     /* two double speed steps */
15251
15252       DOUBLE_PLAYER_SPEED(player);
15253     }
15254
15255     PlayLevelSoundAction(x, y, ACTION_PASSING);
15256   }
15257   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15258   {
15259     RemoveField(x, y);
15260
15261     if (mode != DF_SNAP)
15262     {
15263       GfxElement[x][y] = GFX_ELEMENT(element);
15264       player->is_digging = TRUE;
15265     }
15266
15267     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15268
15269     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15270                                         player->index_bit, dig_side);
15271
15272     if (mode == DF_SNAP)
15273     {
15274 #if USE_NEW_SNAP_DELAY
15275       if (level.block_snap_field)
15276         setFieldForSnapping(x, y, element, move_direction);
15277       else
15278         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15279 #else
15280       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15281 #endif
15282
15283       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15284                                           player->index_bit, dig_side);
15285     }
15286   }
15287   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15288   {
15289     RemoveField(x, y);
15290
15291     if (is_player && mode != DF_SNAP)
15292     {
15293       GfxElement[x][y] = element;
15294       player->is_collecting = TRUE;
15295     }
15296
15297     if (element == EL_SPEED_PILL)
15298     {
15299       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15300     }
15301     else if (element == EL_EXTRA_TIME && level.time > 0)
15302     {
15303       TimeLeft += level.extra_time;
15304
15305 #if 1
15306       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15307
15308       DisplayGameControlValues();
15309 #else
15310       DrawGameValue_Time(TimeLeft);
15311 #endif
15312     }
15313     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15314     {
15315       player->shield_normal_time_left += level.shield_normal_time;
15316       if (element == EL_SHIELD_DEADLY)
15317         player->shield_deadly_time_left += level.shield_deadly_time;
15318     }
15319     else if (element == EL_DYNAMITE ||
15320              element == EL_EM_DYNAMITE ||
15321              element == EL_SP_DISK_RED)
15322     {
15323       if (player->inventory_size < MAX_INVENTORY_SIZE)
15324         player->inventory_element[player->inventory_size++] = element;
15325
15326       DrawGameDoorValues();
15327     }
15328     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15329     {
15330       player->dynabomb_count++;
15331       player->dynabombs_left++;
15332     }
15333     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15334     {
15335       player->dynabomb_size++;
15336     }
15337     else if (element == EL_DYNABOMB_INCREASE_POWER)
15338     {
15339       player->dynabomb_xl = TRUE;
15340     }
15341     else if (IS_KEY(element))
15342     {
15343       player->key[KEY_NR(element)] = TRUE;
15344
15345       DrawGameDoorValues();
15346     }
15347     else if (element == EL_DC_KEY_WHITE)
15348     {
15349       player->num_white_keys++;
15350
15351       /* display white keys? */
15352       /* DrawGameDoorValues(); */
15353     }
15354     else if (IS_ENVELOPE(element))
15355     {
15356       player->show_envelope = element;
15357     }
15358     else if (element == EL_EMC_LENSES)
15359     {
15360       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15361
15362       RedrawAllInvisibleElementsForLenses();
15363     }
15364     else if (element == EL_EMC_MAGNIFIER)
15365     {
15366       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15367
15368       RedrawAllInvisibleElementsForMagnifier();
15369     }
15370     else if (IS_DROPPABLE(element) ||
15371              IS_THROWABLE(element))     /* can be collected and dropped */
15372     {
15373       int i;
15374
15375       if (collect_count == 0)
15376         player->inventory_infinite_element = element;
15377       else
15378         for (i = 0; i < collect_count; i++)
15379           if (player->inventory_size < MAX_INVENTORY_SIZE)
15380             player->inventory_element[player->inventory_size++] = element;
15381
15382       DrawGameDoorValues();
15383     }
15384     else if (collect_count > 0)
15385     {
15386       local_player->gems_still_needed -= collect_count;
15387       if (local_player->gems_still_needed < 0)
15388         local_player->gems_still_needed = 0;
15389
15390 #if 1
15391       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15392
15393       DisplayGameControlValues();
15394 #else
15395       DrawGameValue_Emeralds(local_player->gems_still_needed);
15396 #endif
15397     }
15398
15399     RaiseScoreElement(element);
15400     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15401
15402     if (is_player)
15403       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15404                                           player->index_bit, dig_side);
15405
15406     if (mode == DF_SNAP)
15407     {
15408 #if USE_NEW_SNAP_DELAY
15409       if (level.block_snap_field)
15410         setFieldForSnapping(x, y, element, move_direction);
15411       else
15412         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15413 #else
15414       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15415 #endif
15416
15417       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15418                                           player->index_bit, dig_side);
15419     }
15420   }
15421   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15422   {
15423     if (mode == DF_SNAP && element != EL_BD_ROCK)
15424       return MP_NO_ACTION;
15425
15426     if (CAN_FALL(element) && dy)
15427       return MP_NO_ACTION;
15428
15429     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15430         !(element == EL_SPRING && level.use_spring_bug))
15431       return MP_NO_ACTION;
15432
15433     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15434         ((move_direction & MV_VERTICAL &&
15435           ((element_info[element].move_pattern & MV_LEFT &&
15436             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15437            (element_info[element].move_pattern & MV_RIGHT &&
15438             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15439          (move_direction & MV_HORIZONTAL &&
15440           ((element_info[element].move_pattern & MV_UP &&
15441             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15442            (element_info[element].move_pattern & MV_DOWN &&
15443             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15444       return MP_NO_ACTION;
15445
15446     /* do not push elements already moving away faster than player */
15447     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15448         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15449       return MP_NO_ACTION;
15450
15451     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15452     {
15453       if (player->push_delay_value == -1 || !player_was_pushing)
15454         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15455     }
15456     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15457     {
15458       if (player->push_delay_value == -1)
15459         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15460     }
15461     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15462     {
15463       if (!player->is_pushing)
15464         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15465     }
15466
15467     player->is_pushing = TRUE;
15468     player->is_active = TRUE;
15469
15470     if (!(IN_LEV_FIELD(nextx, nexty) &&
15471           (IS_FREE(nextx, nexty) ||
15472            (IS_SB_ELEMENT(element) &&
15473             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15474            (IS_CUSTOM_ELEMENT(element) &&
15475             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15476       return MP_NO_ACTION;
15477
15478     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15479       return MP_NO_ACTION;
15480
15481     if (player->push_delay == -1)       /* new pushing; restart delay */
15482       player->push_delay = 0;
15483
15484     if (player->push_delay < player->push_delay_value &&
15485         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15486         element != EL_SPRING && element != EL_BALLOON)
15487     {
15488       /* make sure that there is no move delay before next try to push */
15489       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15490         player->move_delay = 0;
15491
15492       return MP_NO_ACTION;
15493     }
15494
15495     if (IS_CUSTOM_ELEMENT(element) &&
15496         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15497     {
15498       if (!DigFieldByCE(nextx, nexty, element))
15499         return MP_NO_ACTION;
15500     }
15501
15502     if (IS_SB_ELEMENT(element))
15503     {
15504       if (element == EL_SOKOBAN_FIELD_FULL)
15505       {
15506         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15507         local_player->sokobanfields_still_needed++;
15508       }
15509
15510       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15511       {
15512         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15513         local_player->sokobanfields_still_needed--;
15514       }
15515
15516       Feld[x][y] = EL_SOKOBAN_OBJECT;
15517
15518       if (Back[x][y] == Back[nextx][nexty])
15519         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15520       else if (Back[x][y] != 0)
15521         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15522                                     ACTION_EMPTYING);
15523       else
15524         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15525                                     ACTION_FILLING);
15526
15527 #if 1
15528       if (local_player->sokobanfields_still_needed == 0 &&
15529           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15530 #else
15531       if (local_player->sokobanfields_still_needed == 0 &&
15532           game.emulation == EMU_SOKOBAN)
15533 #endif
15534       {
15535         PlayerWins(player);
15536
15537         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15538       }
15539     }
15540     else
15541       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15542
15543     InitMovingField(x, y, move_direction);
15544     GfxAction[x][y] = ACTION_PUSHING;
15545
15546     if (mode == DF_SNAP)
15547       ContinueMoving(x, y);
15548     else
15549       MovPos[x][y] = (dx != 0 ? dx : dy);
15550
15551     Pushed[x][y] = TRUE;
15552     Pushed[nextx][nexty] = TRUE;
15553
15554     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15555       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15556     else
15557       player->push_delay_value = -1;    /* get new value later */
15558
15559     /* check for element change _after_ element has been pushed */
15560     if (game.use_change_when_pushing_bug)
15561     {
15562       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15563                                  player->index_bit, dig_side);
15564       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15565                                           player->index_bit, dig_side);
15566     }
15567   }
15568   else if (IS_SWITCHABLE(element))
15569   {
15570     if (PLAYER_SWITCHING(player, x, y))
15571     {
15572       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15573                                           player->index_bit, dig_side);
15574
15575       return MP_ACTION;
15576     }
15577
15578     player->is_switching = TRUE;
15579     player->switch_x = x;
15580     player->switch_y = y;
15581
15582     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15583
15584     if (element == EL_ROBOT_WHEEL)
15585     {
15586       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15587       ZX = x;
15588       ZY = y;
15589
15590       game.robot_wheel_active = TRUE;
15591
15592       TEST_DrawLevelField(x, y);
15593     }
15594     else if (element == EL_SP_TERMINAL)
15595     {
15596       int xx, yy;
15597
15598       SCAN_PLAYFIELD(xx, yy)
15599       {
15600         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15601           Bang(xx, yy);
15602         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15603           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15604       }
15605     }
15606     else if (IS_BELT_SWITCH(element))
15607     {
15608       ToggleBeltSwitch(x, y);
15609     }
15610     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15611              element == EL_SWITCHGATE_SWITCH_DOWN ||
15612              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15613              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15614     {
15615       ToggleSwitchgateSwitch(x, y);
15616     }
15617     else if (element == EL_LIGHT_SWITCH ||
15618              element == EL_LIGHT_SWITCH_ACTIVE)
15619     {
15620       ToggleLightSwitch(x, y);
15621     }
15622     else if (element == EL_TIMEGATE_SWITCH ||
15623              element == EL_DC_TIMEGATE_SWITCH)
15624     {
15625       ActivateTimegateSwitch(x, y);
15626     }
15627     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15628              element == EL_BALLOON_SWITCH_RIGHT ||
15629              element == EL_BALLOON_SWITCH_UP    ||
15630              element == EL_BALLOON_SWITCH_DOWN  ||
15631              element == EL_BALLOON_SWITCH_NONE  ||
15632              element == EL_BALLOON_SWITCH_ANY)
15633     {
15634       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15635                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15636                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15637                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15638                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15639                              move_direction);
15640     }
15641     else if (element == EL_LAMP)
15642     {
15643       Feld[x][y] = EL_LAMP_ACTIVE;
15644       local_player->lights_still_needed--;
15645
15646       ResetGfxAnimation(x, y);
15647       TEST_DrawLevelField(x, y);
15648     }
15649     else if (element == EL_TIME_ORB_FULL)
15650     {
15651       Feld[x][y] = EL_TIME_ORB_EMPTY;
15652
15653       if (level.time > 0 || level.use_time_orb_bug)
15654       {
15655         TimeLeft += level.time_orb_time;
15656
15657 #if 1
15658         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15659
15660         DisplayGameControlValues();
15661 #else
15662         DrawGameValue_Time(TimeLeft);
15663 #endif
15664       }
15665
15666       ResetGfxAnimation(x, y);
15667       TEST_DrawLevelField(x, y);
15668     }
15669     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15670              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15671     {
15672       int xx, yy;
15673
15674       game.ball_state = !game.ball_state;
15675
15676       SCAN_PLAYFIELD(xx, yy)
15677       {
15678         int e = Feld[xx][yy];
15679
15680         if (game.ball_state)
15681         {
15682           if (e == EL_EMC_MAGIC_BALL)
15683             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15684           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15685             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15686         }
15687         else
15688         {
15689           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15690             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15691           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15692             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15693         }
15694       }
15695     }
15696
15697     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15698                                         player->index_bit, dig_side);
15699
15700     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15701                                         player->index_bit, dig_side);
15702
15703     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15704                                         player->index_bit, dig_side);
15705
15706     return MP_ACTION;
15707   }
15708   else
15709   {
15710     if (!PLAYER_SWITCHING(player, x, y))
15711     {
15712       player->is_switching = TRUE;
15713       player->switch_x = x;
15714       player->switch_y = y;
15715
15716       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15717                                  player->index_bit, dig_side);
15718       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15719                                           player->index_bit, dig_side);
15720
15721       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15722                                  player->index_bit, dig_side);
15723       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15724                                           player->index_bit, dig_side);
15725     }
15726
15727     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15728                                player->index_bit, dig_side);
15729     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15730                                         player->index_bit, dig_side);
15731
15732     return MP_NO_ACTION;
15733   }
15734
15735   player->push_delay = -1;
15736
15737   if (is_player)                /* function can also be called by EL_PENGUIN */
15738   {
15739     if (Feld[x][y] != element)          /* really digged/collected something */
15740     {
15741       player->is_collecting = !player->is_digging;
15742       player->is_active = TRUE;
15743     }
15744   }
15745
15746   return MP_MOVING;
15747 }
15748
15749 static boolean DigFieldByCE(int x, int y, int digging_element)
15750 {
15751   int element = Feld[x][y];
15752
15753   if (!IS_FREE(x, y))
15754   {
15755     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15756                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15757                   ACTION_BREAKING);
15758
15759     /* no element can dig solid indestructible elements */
15760     if (IS_INDESTRUCTIBLE(element) &&
15761         !IS_DIGGABLE(element) &&
15762         !IS_COLLECTIBLE(element))
15763       return FALSE;
15764
15765     if (AmoebaNr[x][y] &&
15766         (element == EL_AMOEBA_FULL ||
15767          element == EL_BD_AMOEBA ||
15768          element == EL_AMOEBA_GROWING))
15769     {
15770       AmoebaCnt[AmoebaNr[x][y]]--;
15771       AmoebaCnt2[AmoebaNr[x][y]]--;
15772     }
15773
15774     if (IS_MOVING(x, y))
15775       RemoveMovingField(x, y);
15776     else
15777     {
15778       RemoveField(x, y);
15779       TEST_DrawLevelField(x, y);
15780     }
15781
15782     /* if digged element was about to explode, prevent the explosion */
15783     ExplodeField[x][y] = EX_TYPE_NONE;
15784
15785     PlayLevelSoundAction(x, y, action);
15786   }
15787
15788   Store[x][y] = EL_EMPTY;
15789
15790 #if 1
15791   /* this makes it possible to leave the removed element again */
15792   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15793     Store[x][y] = element;
15794 #else
15795   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15796   {
15797     int move_leave_element = element_info[digging_element].move_leave_element;
15798
15799     /* this makes it possible to leave the removed element again */
15800     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15801                    element : move_leave_element);
15802   }
15803 #endif
15804
15805   return TRUE;
15806 }
15807
15808 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15809 {
15810   int jx = player->jx, jy = player->jy;
15811   int x = jx + dx, y = jy + dy;
15812   int snap_direction = (dx == -1 ? MV_LEFT  :
15813                         dx == +1 ? MV_RIGHT :
15814                         dy == -1 ? MV_UP    :
15815                         dy == +1 ? MV_DOWN  : MV_NONE);
15816   boolean can_continue_snapping = (level.continuous_snapping &&
15817                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15818
15819   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15820     return FALSE;
15821
15822   if (!player->active || !IN_LEV_FIELD(x, y))
15823     return FALSE;
15824
15825   if (dx && dy)
15826     return FALSE;
15827
15828   if (!dx && !dy)
15829   {
15830     if (player->MovPos == 0)
15831       player->is_pushing = FALSE;
15832
15833     player->is_snapping = FALSE;
15834
15835     if (player->MovPos == 0)
15836     {
15837       player->is_moving = FALSE;
15838       player->is_digging = FALSE;
15839       player->is_collecting = FALSE;
15840     }
15841
15842     return FALSE;
15843   }
15844
15845 #if USE_NEW_CONTINUOUS_SNAPPING
15846   /* prevent snapping with already pressed snap key when not allowed */
15847   if (player->is_snapping && !can_continue_snapping)
15848     return FALSE;
15849 #else
15850   if (player->is_snapping)
15851     return FALSE;
15852 #endif
15853
15854   player->MovDir = snap_direction;
15855
15856   if (player->MovPos == 0)
15857   {
15858     player->is_moving = FALSE;
15859     player->is_digging = FALSE;
15860     player->is_collecting = FALSE;
15861   }
15862
15863   player->is_dropping = FALSE;
15864   player->is_dropping_pressed = FALSE;
15865   player->drop_pressed_delay = 0;
15866
15867   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15868     return FALSE;
15869
15870   player->is_snapping = TRUE;
15871   player->is_active = TRUE;
15872
15873   if (player->MovPos == 0)
15874   {
15875     player->is_moving = FALSE;
15876     player->is_digging = FALSE;
15877     player->is_collecting = FALSE;
15878   }
15879
15880   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15881     TEST_DrawLevelField(player->last_jx, player->last_jy);
15882
15883   TEST_DrawLevelField(x, y);
15884
15885   return TRUE;
15886 }
15887
15888 static boolean DropElement(struct PlayerInfo *player)
15889 {
15890   int old_element, new_element;
15891   int dropx = player->jx, dropy = player->jy;
15892   int drop_direction = player->MovDir;
15893   int drop_side = drop_direction;
15894 #if 1
15895   int drop_element = get_next_dropped_element(player);
15896 #else
15897   int drop_element = (player->inventory_size > 0 ?
15898                       player->inventory_element[player->inventory_size - 1] :
15899                       player->inventory_infinite_element != EL_UNDEFINED ?
15900                       player->inventory_infinite_element :
15901                       player->dynabombs_left > 0 ?
15902                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15903                       EL_UNDEFINED);
15904 #endif
15905
15906   player->is_dropping_pressed = TRUE;
15907
15908   /* do not drop an element on top of another element; when holding drop key
15909      pressed without moving, dropped element must move away before the next
15910      element can be dropped (this is especially important if the next element
15911      is dynamite, which can be placed on background for historical reasons) */
15912   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15913     return MP_ACTION;
15914
15915   if (IS_THROWABLE(drop_element))
15916   {
15917     dropx += GET_DX_FROM_DIR(drop_direction);
15918     dropy += GET_DY_FROM_DIR(drop_direction);
15919
15920     if (!IN_LEV_FIELD(dropx, dropy))
15921       return FALSE;
15922   }
15923
15924   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15925   new_element = drop_element;           /* default: no change when dropping */
15926
15927   /* check if player is active, not moving and ready to drop */
15928   if (!player->active || player->MovPos || player->drop_delay > 0)
15929     return FALSE;
15930
15931   /* check if player has anything that can be dropped */
15932   if (new_element == EL_UNDEFINED)
15933     return FALSE;
15934
15935   /* check if drop key was pressed long enough for EM style dynamite */
15936   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15937     return FALSE;
15938
15939   /* check if anything can be dropped at the current position */
15940   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15941     return FALSE;
15942
15943   /* collected custom elements can only be dropped on empty fields */
15944   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15945     return FALSE;
15946
15947   if (old_element != EL_EMPTY)
15948     Back[dropx][dropy] = old_element;   /* store old element on this field */
15949
15950   ResetGfxAnimation(dropx, dropy);
15951   ResetRandomAnimationValue(dropx, dropy);
15952
15953   if (player->inventory_size > 0 ||
15954       player->inventory_infinite_element != EL_UNDEFINED)
15955   {
15956     if (player->inventory_size > 0)
15957     {
15958       player->inventory_size--;
15959
15960       DrawGameDoorValues();
15961
15962       if (new_element == EL_DYNAMITE)
15963         new_element = EL_DYNAMITE_ACTIVE;
15964       else if (new_element == EL_EM_DYNAMITE)
15965         new_element = EL_EM_DYNAMITE_ACTIVE;
15966       else if (new_element == EL_SP_DISK_RED)
15967         new_element = EL_SP_DISK_RED_ACTIVE;
15968     }
15969
15970     Feld[dropx][dropy] = new_element;
15971
15972     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15973       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15974                           el2img(Feld[dropx][dropy]), 0);
15975
15976     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15977
15978     /* needed if previous element just changed to "empty" in the last frame */
15979     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15980
15981     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15982                                player->index_bit, drop_side);
15983     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15984                                         CE_PLAYER_DROPS_X,
15985                                         player->index_bit, drop_side);
15986
15987     TestIfElementTouchesCustomElement(dropx, dropy);
15988   }
15989   else          /* player is dropping a dyna bomb */
15990   {
15991     player->dynabombs_left--;
15992
15993     Feld[dropx][dropy] = new_element;
15994
15995     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15996       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15997                           el2img(Feld[dropx][dropy]), 0);
15998
15999     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16000   }
16001
16002   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16003     InitField_WithBug1(dropx, dropy, FALSE);
16004
16005   new_element = Feld[dropx][dropy];     /* element might have changed */
16006
16007   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16008       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16009   {
16010     int move_direction, nextx, nexty;
16011
16012     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16013       MovDir[dropx][dropy] = drop_direction;
16014
16015     move_direction = MovDir[dropx][dropy];
16016     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16017     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16018
16019     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16020
16021 #if USE_FIX_IMPACT_COLLISION
16022     /* do not cause impact style collision by dropping elements that can fall */
16023     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16024 #else
16025     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16026 #endif
16027   }
16028
16029   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16030   player->is_dropping = TRUE;
16031
16032   player->drop_pressed_delay = 0;
16033   player->is_dropping_pressed = FALSE;
16034
16035   player->drop_x = dropx;
16036   player->drop_y = dropy;
16037
16038   return TRUE;
16039 }
16040
16041 /* ------------------------------------------------------------------------- */
16042 /* game sound playing functions                                              */
16043 /* ------------------------------------------------------------------------- */
16044
16045 static int *loop_sound_frame = NULL;
16046 static int *loop_sound_volume = NULL;
16047
16048 void InitPlayLevelSound()
16049 {
16050   int num_sounds = getSoundListSize();
16051
16052   checked_free(loop_sound_frame);
16053   checked_free(loop_sound_volume);
16054
16055   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16056   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16057 }
16058
16059 static void PlayLevelSound(int x, int y, int nr)
16060 {
16061   int sx = SCREENX(x), sy = SCREENY(y);
16062   int volume, stereo_position;
16063   int max_distance = 8;
16064   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16065
16066   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16067       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16068     return;
16069
16070   if (!IN_LEV_FIELD(x, y) ||
16071       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16072       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16073     return;
16074
16075   volume = SOUND_MAX_VOLUME;
16076
16077   if (!IN_SCR_FIELD(sx, sy))
16078   {
16079     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16080     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16081
16082     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16083   }
16084
16085   stereo_position = (SOUND_MAX_LEFT +
16086                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16087                      (SCR_FIELDX + 2 * max_distance));
16088
16089   if (IS_LOOP_SOUND(nr))
16090   {
16091     /* This assures that quieter loop sounds do not overwrite louder ones,
16092        while restarting sound volume comparison with each new game frame. */
16093
16094     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16095       return;
16096
16097     loop_sound_volume[nr] = volume;
16098     loop_sound_frame[nr] = FrameCounter;
16099   }
16100
16101   PlaySoundExt(nr, volume, stereo_position, type);
16102 }
16103
16104 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16105 {
16106   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16107                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16108                  y < LEVELY(BY1) ? LEVELY(BY1) :
16109                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16110                  sound_action);
16111 }
16112
16113 static void PlayLevelSoundAction(int x, int y, int action)
16114 {
16115   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16116 }
16117
16118 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16119 {
16120   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16121
16122   if (sound_effect != SND_UNDEFINED)
16123     PlayLevelSound(x, y, sound_effect);
16124 }
16125
16126 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16127                                               int action)
16128 {
16129   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16130
16131   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16132     PlayLevelSound(x, y, sound_effect);
16133 }
16134
16135 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16136 {
16137   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16138
16139   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16140     PlayLevelSound(x, y, sound_effect);
16141 }
16142
16143 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16144 {
16145   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16146
16147   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16148     StopSound(sound_effect);
16149 }
16150
16151 static void PlayLevelMusic()
16152 {
16153   if (levelset.music[level_nr] != MUS_UNDEFINED)
16154     PlayMusic(levelset.music[level_nr]);        /* from config file */
16155   else
16156     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16157 }
16158
16159 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16160 {
16161   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16162   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16163   int x = xx - 1 - offset;
16164   int y = yy - 1 - offset;
16165
16166   switch (sample)
16167   {
16168     case SAMPLE_blank:
16169       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16170       break;
16171
16172     case SAMPLE_roll:
16173       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16174       break;
16175
16176     case SAMPLE_stone:
16177       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16178       break;
16179
16180     case SAMPLE_nut:
16181       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16182       break;
16183
16184     case SAMPLE_crack:
16185       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16186       break;
16187
16188     case SAMPLE_bug:
16189       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16190       break;
16191
16192     case SAMPLE_tank:
16193       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16194       break;
16195
16196     case SAMPLE_android_clone:
16197       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16198       break;
16199
16200     case SAMPLE_android_move:
16201       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16202       break;
16203
16204     case SAMPLE_spring:
16205       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16206       break;
16207
16208     case SAMPLE_slurp:
16209       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16210       break;
16211
16212     case SAMPLE_eater:
16213       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16214       break;
16215
16216     case SAMPLE_eater_eat:
16217       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16218       break;
16219
16220     case SAMPLE_alien:
16221       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16222       break;
16223
16224     case SAMPLE_collect:
16225       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16226       break;
16227
16228     case SAMPLE_diamond:
16229       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16230       break;
16231
16232     case SAMPLE_squash:
16233       /* !!! CHECK THIS !!! */
16234 #if 1
16235       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16236 #else
16237       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16238 #endif
16239       break;
16240
16241     case SAMPLE_wonderfall:
16242       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16243       break;
16244
16245     case SAMPLE_drip:
16246       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16247       break;
16248
16249     case SAMPLE_push:
16250       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16251       break;
16252
16253     case SAMPLE_dirt:
16254       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16255       break;
16256
16257     case SAMPLE_acid:
16258       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16259       break;
16260
16261     case SAMPLE_ball:
16262       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16263       break;
16264
16265     case SAMPLE_grow:
16266       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16267       break;
16268
16269     case SAMPLE_wonder:
16270       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16271       break;
16272
16273     case SAMPLE_door:
16274       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16275       break;
16276
16277     case SAMPLE_exit_open:
16278       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16279       break;
16280
16281     case SAMPLE_exit_leave:
16282       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16283       break;
16284
16285     case SAMPLE_dynamite:
16286       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16287       break;
16288
16289     case SAMPLE_tick:
16290       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16291       break;
16292
16293     case SAMPLE_press:
16294       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16295       break;
16296
16297     case SAMPLE_wheel:
16298       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16299       break;
16300
16301     case SAMPLE_boom:
16302       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16303       break;
16304
16305     case SAMPLE_die:
16306       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16307       break;
16308
16309     case SAMPLE_time:
16310       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16311       break;
16312
16313     default:
16314       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16315       break;
16316   }
16317 }
16318
16319 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16320 {
16321   int element = map_element_SP_to_RND(element_sp);
16322   int action = map_action_SP_to_RND(action_sp);
16323   int offset = (setup.sp_show_border_elements ? 0 : 1);
16324   int x = xx - offset;
16325   int y = yy - offset;
16326
16327 #if 0
16328   printf("::: %d -> %d\n", element_sp, action_sp);
16329 #endif
16330
16331   PlayLevelSoundElementAction(x, y, element, action);
16332 }
16333
16334 #if 0
16335 void ChangeTime(int value)
16336 {
16337   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16338
16339   *time += value;
16340
16341   /* EMC game engine uses value from time counter of RND game engine */
16342   level.native_em_level->lev->time = *time;
16343
16344   DrawGameValue_Time(*time);
16345 }
16346
16347 void RaiseScore(int value)
16348 {
16349   /* EMC game engine and RND game engine have separate score counters */
16350   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16351                 &level.native_em_level->lev->score : &local_player->score);
16352
16353   *score += value;
16354
16355   DrawGameValue_Score(*score);
16356 }
16357 #endif
16358
16359 void RaiseScore(int value)
16360 {
16361   local_player->score += value;
16362
16363 #if 1
16364   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16365
16366   DisplayGameControlValues();
16367 #else
16368   DrawGameValue_Score(local_player->score);
16369 #endif
16370 }
16371
16372 void RaiseScoreElement(int element)
16373 {
16374   switch (element)
16375   {
16376     case EL_EMERALD:
16377     case EL_BD_DIAMOND:
16378     case EL_EMERALD_YELLOW:
16379     case EL_EMERALD_RED:
16380     case EL_EMERALD_PURPLE:
16381     case EL_SP_INFOTRON:
16382       RaiseScore(level.score[SC_EMERALD]);
16383       break;
16384     case EL_DIAMOND:
16385       RaiseScore(level.score[SC_DIAMOND]);
16386       break;
16387     case EL_CRYSTAL:
16388       RaiseScore(level.score[SC_CRYSTAL]);
16389       break;
16390     case EL_PEARL:
16391       RaiseScore(level.score[SC_PEARL]);
16392       break;
16393     case EL_BUG:
16394     case EL_BD_BUTTERFLY:
16395     case EL_SP_ELECTRON:
16396       RaiseScore(level.score[SC_BUG]);
16397       break;
16398     case EL_SPACESHIP:
16399     case EL_BD_FIREFLY:
16400     case EL_SP_SNIKSNAK:
16401       RaiseScore(level.score[SC_SPACESHIP]);
16402       break;
16403     case EL_YAMYAM:
16404     case EL_DARK_YAMYAM:
16405       RaiseScore(level.score[SC_YAMYAM]);
16406       break;
16407     case EL_ROBOT:
16408       RaiseScore(level.score[SC_ROBOT]);
16409       break;
16410     case EL_PACMAN:
16411       RaiseScore(level.score[SC_PACMAN]);
16412       break;
16413     case EL_NUT:
16414       RaiseScore(level.score[SC_NUT]);
16415       break;
16416     case EL_DYNAMITE:
16417     case EL_EM_DYNAMITE:
16418     case EL_SP_DISK_RED:
16419     case EL_DYNABOMB_INCREASE_NUMBER:
16420     case EL_DYNABOMB_INCREASE_SIZE:
16421     case EL_DYNABOMB_INCREASE_POWER:
16422       RaiseScore(level.score[SC_DYNAMITE]);
16423       break;
16424     case EL_SHIELD_NORMAL:
16425     case EL_SHIELD_DEADLY:
16426       RaiseScore(level.score[SC_SHIELD]);
16427       break;
16428     case EL_EXTRA_TIME:
16429       RaiseScore(level.extra_time_score);
16430       break;
16431     case EL_KEY_1:
16432     case EL_KEY_2:
16433     case EL_KEY_3:
16434     case EL_KEY_4:
16435     case EL_EM_KEY_1:
16436     case EL_EM_KEY_2:
16437     case EL_EM_KEY_3:
16438     case EL_EM_KEY_4:
16439     case EL_EMC_KEY_5:
16440     case EL_EMC_KEY_6:
16441     case EL_EMC_KEY_7:
16442     case EL_EMC_KEY_8:
16443     case EL_DC_KEY_WHITE:
16444       RaiseScore(level.score[SC_KEY]);
16445       break;
16446     default:
16447       RaiseScore(element_info[element].collect_score);
16448       break;
16449   }
16450 }
16451
16452 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16453 {
16454   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16455   {
16456 #if defined(NETWORK_AVALIABLE)
16457     if (options.network)
16458       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16459     else
16460 #endif
16461     {
16462       if (quick_quit)
16463       {
16464 #if 1
16465
16466 #if 1
16467         FadeSkipNextFadeIn();
16468 #else
16469         fading = fading_none;
16470 #endif
16471
16472 #else
16473         OpenDoor(DOOR_CLOSE_1);
16474 #endif
16475
16476         game_status = GAME_MODE_MAIN;
16477
16478 #if 1
16479         DrawAndFadeInMainMenu(REDRAW_FIELD);
16480 #else
16481         DrawMainMenu();
16482 #endif
16483       }
16484       else
16485       {
16486 #if 0
16487         FadeOut(REDRAW_FIELD);
16488 #endif
16489
16490         game_status = GAME_MODE_MAIN;
16491
16492         DrawAndFadeInMainMenu(REDRAW_FIELD);
16493       }
16494     }
16495   }
16496   else          /* continue playing the game */
16497   {
16498     if (tape.playing && tape.deactivate_display)
16499       TapeDeactivateDisplayOff(TRUE);
16500
16501     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16502
16503     if (tape.playing && tape.deactivate_display)
16504       TapeDeactivateDisplayOn();
16505   }
16506 }
16507
16508 void RequestQuitGame(boolean ask_if_really_quit)
16509 {
16510   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16511   boolean skip_request = AllPlayersGone || quick_quit;
16512
16513   RequestQuitGameExt(skip_request, quick_quit,
16514                      "Do you really want to quit the game ?");
16515 }
16516
16517
16518 /* ------------------------------------------------------------------------- */
16519 /* random generator functions                                                */
16520 /* ------------------------------------------------------------------------- */
16521
16522 unsigned int InitEngineRandom_RND(long seed)
16523 {
16524   game.num_random_calls = 0;
16525
16526 #if 0
16527   unsigned int rnd_seed = InitEngineRandom(seed);
16528
16529   printf("::: START RND: %d\n", rnd_seed);
16530
16531   return rnd_seed;
16532 #else
16533
16534   return InitEngineRandom(seed);
16535
16536 #endif
16537
16538 }
16539
16540 unsigned int RND(int max)
16541 {
16542   if (max > 0)
16543   {
16544     game.num_random_calls++;
16545
16546     return GetEngineRandom(max);
16547   }
16548
16549   return 0;
16550 }
16551
16552
16553 /* ------------------------------------------------------------------------- */
16554 /* game engine snapshot handling functions                                   */
16555 /* ------------------------------------------------------------------------- */
16556
16557 struct EngineSnapshotInfo
16558 {
16559   /* runtime values for custom element collect score */
16560   int collect_score[NUM_CUSTOM_ELEMENTS];
16561
16562   /* runtime values for group element choice position */
16563   int choice_pos[NUM_GROUP_ELEMENTS];
16564
16565   /* runtime values for belt position animations */
16566   int belt_graphic[4][NUM_BELT_PARTS];
16567   int belt_anim_mode[4][NUM_BELT_PARTS];
16568 };
16569
16570 static struct EngineSnapshotInfo engine_snapshot_rnd;
16571 static char *snapshot_level_identifier = NULL;
16572 static int snapshot_level_nr = -1;
16573
16574 static void SaveEngineSnapshotValues_RND()
16575 {
16576   static int belt_base_active_element[4] =
16577   {
16578     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16579     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16580     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16581     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16582   };
16583   int i, j;
16584
16585   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16586   {
16587     int element = EL_CUSTOM_START + i;
16588
16589     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16590   }
16591
16592   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16593   {
16594     int element = EL_GROUP_START + i;
16595
16596     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16597   }
16598
16599   for (i = 0; i < 4; i++)
16600   {
16601     for (j = 0; j < NUM_BELT_PARTS; j++)
16602     {
16603       int element = belt_base_active_element[i] + j;
16604       int graphic = el2img(element);
16605       int anim_mode = graphic_info[graphic].anim_mode;
16606
16607       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16608       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16609     }
16610   }
16611 }
16612
16613 static void LoadEngineSnapshotValues_RND()
16614 {
16615   unsigned long num_random_calls = game.num_random_calls;
16616   int i, j;
16617
16618   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16619   {
16620     int element = EL_CUSTOM_START + i;
16621
16622     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16623   }
16624
16625   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16626   {
16627     int element = EL_GROUP_START + i;
16628
16629     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16630   }
16631
16632   for (i = 0; i < 4; i++)
16633   {
16634     for (j = 0; j < NUM_BELT_PARTS; j++)
16635     {
16636       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16637       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16638
16639       graphic_info[graphic].anim_mode = anim_mode;
16640     }
16641   }
16642
16643   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16644   {
16645     InitRND(tape.random_seed);
16646     for (i = 0; i < num_random_calls; i++)
16647       RND(1);
16648   }
16649
16650   if (game.num_random_calls != num_random_calls)
16651   {
16652     Error(ERR_INFO, "number of random calls out of sync");
16653     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16654     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16655     Error(ERR_EXIT, "this should not happen -- please debug");
16656   }
16657 }
16658
16659 void SaveEngineSnapshot()
16660 {
16661   /* do not save snapshots from editor */
16662   if (level_editor_test_game)
16663     return;
16664
16665   /* free previous snapshot buffers, if needed */
16666   FreeEngineSnapshotBuffers();
16667
16668   /* copy some special values to a structure better suited for the snapshot */
16669
16670   SaveEngineSnapshotValues_RND();
16671   SaveEngineSnapshotValues_EM();
16672   SaveEngineSnapshotValues_SP();
16673
16674   /* save values stored in special snapshot structure */
16675
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16679
16680   /* save further RND engine values */
16681
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16685
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16690
16691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16694   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16696
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16700
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16702
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16704
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16707
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16723   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16726
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16729
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16733
16734   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16736
16737   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16741   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16742
16743   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16745
16746   /* save level identification information */
16747
16748   setString(&snapshot_level_identifier, leveldir_current->identifier);
16749   snapshot_level_nr = level_nr;
16750
16751 #if 0
16752   ListNode *node = engine_snapshot_list_rnd;
16753   int num_bytes = 0;
16754
16755   while (node != NULL)
16756   {
16757     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16758
16759     node = node->next;
16760   }
16761
16762   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16763 #endif
16764 }
16765
16766 void LoadEngineSnapshot()
16767 {
16768   /* restore generically stored snapshot buffers */
16769
16770   LoadEngineSnapshotBuffers();
16771
16772   /* restore special values from snapshot structure */
16773
16774   LoadEngineSnapshotValues_RND();
16775   LoadEngineSnapshotValues_EM();
16776   LoadEngineSnapshotValues_SP();
16777 }
16778
16779 boolean CheckEngineSnapshot()
16780 {
16781   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16782           snapshot_level_nr == level_nr);
16783 }
16784
16785
16786 /* ---------- new game button stuff ---------------------------------------- */
16787
16788 static struct
16789 {
16790   int graphic;
16791   struct Rect *pos;
16792   int gadget_id;
16793   char *infotext;
16794 } gamebutton_info[NUM_GAME_BUTTONS] =
16795 {
16796   {
16797     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16798     GAME_CTRL_ID_STOP,                  "stop game"
16799   },
16800   {
16801     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16802     GAME_CTRL_ID_PAUSE,                 "pause game"
16803   },
16804   {
16805     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16806     GAME_CTRL_ID_PLAY,                  "play game"
16807   },
16808   {
16809     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16810     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16811   },
16812   {
16813     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16814     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16815   },
16816   {
16817     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16818     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16819   }
16820 };
16821
16822 void CreateGameButtons()
16823 {
16824   int i;
16825
16826   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16827   {
16828     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16829     struct Rect *pos = gamebutton_info[i].pos;
16830     struct GadgetInfo *gi;
16831     int button_type;
16832     boolean checked;
16833     unsigned long event_mask;
16834     int gd_x   = gfx->src_x;
16835     int gd_y   = gfx->src_y;
16836     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16837     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16838     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16839     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16840     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16841     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16842     int id = i;
16843
16844     if (id == GAME_CTRL_ID_STOP ||
16845         id == GAME_CTRL_ID_PAUSE ||
16846         id == GAME_CTRL_ID_PLAY)
16847     {
16848       button_type = GD_TYPE_NORMAL_BUTTON;
16849       checked = FALSE;
16850       event_mask = GD_EVENT_RELEASED;
16851     }
16852     else
16853     {
16854       button_type = GD_TYPE_CHECK_BUTTON;
16855       checked =
16856         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16857          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16858          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16859       event_mask = GD_EVENT_PRESSED;
16860     }
16861
16862     gi = CreateGadget(GDI_CUSTOM_ID, id,
16863                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16864                       GDI_X, DX + pos->x,
16865                       GDI_Y, DY + pos->y,
16866                       GDI_WIDTH, gfx->width,
16867                       GDI_HEIGHT, gfx->height,
16868                       GDI_TYPE, button_type,
16869                       GDI_STATE, GD_BUTTON_UNPRESSED,
16870                       GDI_CHECKED, checked,
16871                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16872                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16873                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16874                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16875                       GDI_DIRECT_DRAW, FALSE,
16876                       GDI_EVENT_MASK, event_mask,
16877                       GDI_CALLBACK_ACTION, HandleGameButtons,
16878                       GDI_END);
16879
16880     if (gi == NULL)
16881       Error(ERR_EXIT, "cannot create gadget");
16882
16883     game_gadget[id] = gi;
16884   }
16885 }
16886
16887 void FreeGameButtons()
16888 {
16889   int i;
16890
16891   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16892     FreeGadget(game_gadget[i]);
16893 }
16894
16895 static void MapGameButtons()
16896 {
16897   int i;
16898
16899   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16900     MapGadget(game_gadget[i]);
16901 }
16902
16903 void UnmapGameButtons()
16904 {
16905   int i;
16906
16907   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16908     UnmapGadget(game_gadget[i]);
16909 }
16910
16911 void RedrawGameButtons()
16912 {
16913   int i;
16914
16915   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16916     RedrawGadget(game_gadget[i]);
16917 }
16918
16919 static void HandleGameButtonsExt(int id)
16920 {
16921   if (game_status != GAME_MODE_PLAYING)
16922     return;
16923
16924   switch (id)
16925   {
16926     case GAME_CTRL_ID_STOP:
16927       if (tape.playing)
16928         TapeStop();
16929       else
16930         RequestQuitGame(TRUE);
16931       break;
16932
16933     case GAME_CTRL_ID_PAUSE:
16934       if (options.network)
16935       {
16936 #if defined(NETWORK_AVALIABLE)
16937         if (tape.pausing)
16938           SendToServer_ContinuePlaying();
16939         else
16940           SendToServer_PausePlaying();
16941 #endif
16942       }
16943       else
16944         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16945       break;
16946
16947     case GAME_CTRL_ID_PLAY:
16948       if (tape.pausing)
16949       {
16950 #if defined(NETWORK_AVALIABLE)
16951         if (options.network)
16952           SendToServer_ContinuePlaying();
16953         else
16954 #endif
16955         {
16956           tape.pausing = FALSE;
16957           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16958         }
16959       }
16960       break;
16961
16962     case SOUND_CTRL_ID_MUSIC:
16963       if (setup.sound_music)
16964       { 
16965         setup.sound_music = FALSE;
16966
16967         FadeMusic();
16968       }
16969       else if (audio.music_available)
16970       { 
16971         setup.sound = setup.sound_music = TRUE;
16972
16973         SetAudioMode(setup.sound);
16974
16975         PlayLevelMusic();
16976       }
16977       break;
16978
16979     case SOUND_CTRL_ID_LOOPS:
16980       if (setup.sound_loops)
16981         setup.sound_loops = FALSE;
16982       else if (audio.loops_available)
16983       {
16984         setup.sound = setup.sound_loops = TRUE;
16985
16986         SetAudioMode(setup.sound);
16987       }
16988       break;
16989
16990     case SOUND_CTRL_ID_SIMPLE:
16991       if (setup.sound_simple)
16992         setup.sound_simple = FALSE;
16993       else if (audio.sound_available)
16994       {
16995         setup.sound = setup.sound_simple = TRUE;
16996
16997         SetAudioMode(setup.sound);
16998       }
16999       break;
17000
17001     default:
17002       break;
17003   }
17004 }
17005
17006 static void HandleGameButtons(struct GadgetInfo *gi)
17007 {
17008   HandleGameButtonsExt(gi->custom_id);
17009 }
17010
17011 void HandleSoundButtonKeys(Key key)
17012 {
17013 #if 1
17014   if (key == setup.shortcut.sound_simple)
17015     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17016   else if (key == setup.shortcut.sound_loops)
17017     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17018   else if (key == setup.shortcut.sound_music)
17019     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17020 #else
17021   if (key == setup.shortcut.sound_simple)
17022     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17023   else if (key == setup.shortcut.sound_loops)
17024     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17025   else if (key == setup.shortcut.sound_music)
17026     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17027 #endif
17028 }