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