rnd-20100708-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_FRAME                        35
198 #define GAME_PANEL_SHIELD_NORMAL                36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
200 #define GAME_PANEL_SHIELD_DEADLY                38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
202 #define GAME_PANEL_EXIT                         40
203 #define GAME_PANEL_EMC_MAGIC_BALL               41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
205 #define GAME_PANEL_LIGHT_SWITCH                 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
207 #define GAME_PANEL_TIMEGATE_SWITCH              45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
209 #define GAME_PANEL_SWITCHGATE_SWITCH            47
210 #define GAME_PANEL_EMC_LENSES                   48
211 #define GAME_PANEL_EMC_LENSES_TIME              49
212 #define GAME_PANEL_EMC_MAGNIFIER                50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
214 #define GAME_PANEL_BALLOON_SWITCH               52
215 #define GAME_PANEL_DYNABOMB_NUMBER              53
216 #define GAME_PANEL_DYNABOMB_SIZE                54
217 #define GAME_PANEL_DYNABOMB_POWER               55
218 #define GAME_PANEL_PENGUINS                     56
219 #define GAME_PANEL_SOKOBAN_OBJECTS              57
220 #define GAME_PANEL_SOKOBAN_FIELDS               58
221 #define GAME_PANEL_ROBOT_WHEEL                  59
222 #define GAME_PANEL_CONVEYOR_BELT_1              60
223 #define GAME_PANEL_CONVEYOR_BELT_2              61
224 #define GAME_PANEL_CONVEYOR_BELT_3              62
225 #define GAME_PANEL_CONVEYOR_BELT_4              63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
230 #define GAME_PANEL_MAGIC_WALL                   68
231 #define GAME_PANEL_MAGIC_WALL_TIME              69
232 #define GAME_PANEL_GRAVITY_STATE                70
233 #define GAME_PANEL_GRAPHIC_1                    71
234 #define GAME_PANEL_GRAPHIC_2                    72
235 #define GAME_PANEL_GRAPHIC_3                    73
236 #define GAME_PANEL_GRAPHIC_4                    74
237 #define GAME_PANEL_GRAPHIC_5                    75
238 #define GAME_PANEL_GRAPHIC_6                    76
239 #define GAME_PANEL_GRAPHIC_7                    77
240 #define GAME_PANEL_GRAPHIC_8                    78
241 #define GAME_PANEL_ELEMENT_1                    79
242 #define GAME_PANEL_ELEMENT_2                    80
243 #define GAME_PANEL_ELEMENT_3                    81
244 #define GAME_PANEL_ELEMENT_4                    82
245 #define GAME_PANEL_ELEMENT_5                    83
246 #define GAME_PANEL_ELEMENT_6                    84
247 #define GAME_PANEL_ELEMENT_7                    85
248 #define GAME_PANEL_ELEMENT_8                    86
249 #define GAME_PANEL_ELEMENT_COUNT_1              87
250 #define GAME_PANEL_ELEMENT_COUNT_2              88
251 #define GAME_PANEL_ELEMENT_COUNT_3              89
252 #define GAME_PANEL_ELEMENT_COUNT_4              90
253 #define GAME_PANEL_ELEMENT_COUNT_5              91
254 #define GAME_PANEL_ELEMENT_COUNT_6              92
255 #define GAME_PANEL_ELEMENT_COUNT_7              93
256 #define GAME_PANEL_ELEMENT_COUNT_8              94
257 #define GAME_PANEL_CE_SCORE_1                   95
258 #define GAME_PANEL_CE_SCORE_2                   96
259 #define GAME_PANEL_CE_SCORE_3                   97
260 #define GAME_PANEL_CE_SCORE_4                   98
261 #define GAME_PANEL_CE_SCORE_5                   99
262 #define GAME_PANEL_CE_SCORE_6                   100
263 #define GAME_PANEL_CE_SCORE_7                   101
264 #define GAME_PANEL_CE_SCORE_8                   102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
273 #define GAME_PANEL_PLAYER_NAME                  111
274 #define GAME_PANEL_LEVEL_NAME                   112
275 #define GAME_PANEL_LEVEL_AUTHOR                 113
276
277 #define NUM_GAME_PANEL_CONTROLS                 114
278
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289   int nr;
290
291   struct TextPosInfo *pos;
292   int type;
293
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING      3
884 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION   2
886 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF  -1
890 #define INITIAL_MOVE_DELAY_ON   0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED    32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED   4
896 #define MOVE_DELAY_MAX_SPEED    1
897
898 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN       (1)
907 #define MOVE_STEPSIZE_MAX       (TILEX)
908
909 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911
912 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
915                                  RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
917                                  RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
919                                  RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
921                                     (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
923                                  RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
926                                  RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
928                                  RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
932          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
935         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
936          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
937          (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p)                                         \
940         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
943         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
944          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
945          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
946          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
947          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
948          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
949          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
950          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
951          (e))
952
953 #define CAN_GROW_INTO(e)                                                \
954         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
957                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
968                                         (CAN_MOVE_INTO_ACID(e) &&       \
969                                          Feld[x][y] == EL_ACID) ||      \
970                                         (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
973                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
974                                         (condition) ||                  \
975                                         (CAN_MOVE_INTO_ACID(e) &&       \
976                                          Feld[x][y] == EL_ACID) ||      \
977                                         (DONT_COLLIDE_WITH(e) &&        \
978                                          IS_PLAYER(x, y) &&             \
979                                          !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
991         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1012                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1019         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1022         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1025         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1026                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1031                 (!IS_PLAYER(x, y) &&                                    \
1032                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1035         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP               0
1047 #define GAME_CTRL_ID_PAUSE              1
1048 #define GAME_CTRL_ID_PLAY               2
1049 #define SOUND_CTRL_ID_MUSIC             3
1050 #define SOUND_CTRL_ID_LOOPS             4
1051 #define SOUND_CTRL_ID_SIMPLE            5
1052
1053 #define NUM_GAME_BUTTONS                6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)                             \
1105         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1107         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1109         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1156 {                                                                       \
1157   if (recursion_loop_detected)                                          \
1158     return (rc);                                                        \
1159                                                                         \
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1161   {                                                                     \
1162     recursion_loop_detected = TRUE;                                     \
1163     recursion_loop_element = (e);                                       \
1164   }                                                                     \
1165                                                                         \
1166   recursion_loop_depth++;                                               \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END()                                  \
1170 {                                                                       \
1171   recursion_loop_depth--;                                               \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,                  0, 0 },
1484   { EL_BALLOON,                 0, 0 },
1485
1486   { EL_SOKOBAN_OBJECT,          2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1488   { EL_SATELLITE,               2, 0 },
1489   { EL_SP_DISK_YELLOW,          2, 0 },
1490
1491   { EL_UNDEFINED,               0, 0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,             2 },
1502   { EL_AMOEBA_DROPPING,         2 },
1503   { EL_QUICKSAND_FILLING,       1 },
1504   { EL_QUICKSAND_EMPTYING,      1 },
1505   { EL_QUICKSAND_FAST_FILLING,  2 },
1506   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507   { EL_MAGIC_WALL_FILLING,      2 },
1508   { EL_MAGIC_WALL_EMPTYING,     2 },
1509   { EL_BD_MAGIC_WALL_FILLING,   2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1511   { EL_DC_MAGIC_WALL_FILLING,   2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1513
1514   { EL_UNDEFINED,               0 },
1515 };
1516
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,                 1 },
1525   { EL_BD_DIAMOND,              1 },
1526   { EL_EMERALD_YELLOW,          1 },
1527   { EL_EMERALD_RED,             1 },
1528   { EL_EMERALD_PURPLE,          1 },
1529   { EL_DIAMOND,                 3 },
1530   { EL_SP_INFOTRON,             1 },
1531   { EL_PEARL,                   5 },
1532   { EL_CRYSTAL,                 8 },
1533
1534   { EL_UNDEFINED,               0 },
1535 };
1536
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1555
1556   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1558   { EL_SP_PORT_UP,                                                   MV_DOWN },
1559   { EL_SP_PORT_DOWN,                                         MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1575
1576   { EL_UNDEFINED,                       MV_NONE                              }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1584                                  IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1595                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1596                                      (y) += playfield_scan_delta_y)     \
1597                                 for ((x) = playfield_scan_start_x;      \
1598                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1599                                      (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609         EL_DYNAMITE;
1610 }
1611 #endif
1612
1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632
1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638   InitPlayfieldScanModeVars();
1639 }
1640
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648
1649   return TILEX / move_stepsize;
1650 }
1651
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653                                boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670     player->move_delay_value_next = -1;
1671
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675
1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697
1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710                                     group->choice_mode, 0,
1711                                     group->choice_pos);
1712
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715
1716     group->choice_pos++;
1717
1718     element = group->element_resolved[element_pos];
1719   }
1720
1721   return element;
1722 }
1723
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732         Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734         return;
1735       }
1736       else
1737       {
1738         stored_player[0].initial_element = element;
1739         stored_player[0].use_murphy = TRUE;
1740
1741         if (!level.use_artwork_element[0])
1742           stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753
1754     player->present = TRUE;
1755
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757                                 level.sp_block_last_field :
1758                                 level.block_last_field);
1759
1760     /* ---------- initialize player's last field block delay --------------- */
1761
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779         StorePlayer[jx][jy] = 0;
1780
1781       StorePlayer[x][y] = Feld[x][y];
1782
1783       if (options.debug)
1784       {
1785         printf("Player %d activated.\n", player->element_nr);
1786         printf("[Local player is %d and currently %s.]\n",
1787                local_player->element_nr,
1788                local_player->active ? "active" : "not active");
1789       }
1790     }
1791
1792     Feld[x][y] = EL_EMPTY;
1793
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809
1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899         Feld[x][y] = EL_AMOEBA_GROWING;
1900         Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1949         {
1950           game.belt_dir[belt_nr] = belt_dir;
1951           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952         }
1953         else    /* more than one switch -- set it like the first switch */
1954         {
1955           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956         }
1957       }
1958       break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1962       if (init_game)
1963         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1967       if (init_game)
1968         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981           game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026         if (CAN_MOVE(element))
2027           InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036         Feld[x][y] = GetElementFromGroupElement(element);
2037
2038         InitField(x, y, init_game);
2039       }
2040
2041       break;
2042   }
2043
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061
2062   InitField(x, y, init_game);
2063
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078
2079 #if 1
2080
2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085                           EL_EM_KEY_1 : EL_KEY_1);
2086
2087   return key_base_element + key_nr;
2088 }
2089
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093           player->inventory_element[player->inventory_size - 1] :
2094           player->inventory_infinite_element != EL_UNDEFINED ?
2095           player->inventory_infinite_element :
2096           player->dynabombs_left > 0 ?
2097           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098           EL_UNDEFINED);
2099 }
2100
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112     return (player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             player->inventory_infinite_element != EL_UNDEFINED ?
2115             player->inventory_infinite_element :
2116             player->dynabombs_left >= min_dynabombs_left ?
2117             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118             EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127             player->inventory_infinite_element :
2128             player->dynabombs_left >= min_dynabombs_left ?
2129             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130             player->inventory_size >= min_inventory_size ?
2131             player->inventory_element[inventory_pos] :
2132             EL_UNDEFINED);
2133   }
2134 }
2135
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146
2147   return compare_result;
2148 }
2149
2150 void InitGameControlValues()
2151 {
2152   int i;
2153
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226   int time = (local_player->LevelSolved ?
2227               local_player->LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->time_played :
2232               level.time == 0 ? TimePlayed : TimeLeft);
2233   int score = (local_player->LevelSolved ?
2234                local_player->LevelSolved_CountingScore :
2235                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                level.native_em_level->lev->score :
2237                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                level.native_sp_level->game_sp->score :
2239                local_player->score);
2240   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241               level.native_em_level->lev->required :
2242               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243               level.native_sp_level->game_sp->infotrons_still_needed :
2244               local_player->gems_still_needed);
2245   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                      level.native_em_level->lev->required > 0 :
2247                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249                      local_player->gems_still_needed > 0 ||
2250                      local_player->sokobanfields_still_needed > 0 ||
2251                      local_player->lights_still_needed > 0);
2252
2253   UpdatePlayfieldElementCount();
2254
2255   /* update game panel control values */
2256
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       /* only one player in Supaplex game engine */
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289           level.native_em_level->ply[i]->dynamite;
2290       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292           level.native_sp_level->game_sp->red_disk_count;
2293       else
2294         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295           stored_player[i].inventory_size;
2296
2297       if (stored_player[i].num_white_keys > 0)
2298         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299           EL_DC_KEY_WHITE;
2300
2301       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302         stored_player[i].num_white_keys;
2303     }
2304   }
2305   else
2306   {
2307     int player_nr = game.centered_player_nr;
2308
2309     for (k = 0; k < MAX_NUM_KEYS; k++)
2310     {
2311       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312       {
2313         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315             get_key_element_from_nr(k);
2316       }
2317       else if (stored_player[player_nr].key[k])
2318         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319           get_key_element_from_nr(k);
2320     }
2321
2322     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         level.native_em_level->ply[player_nr]->dynamite;
2325     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327         level.native_sp_level->game_sp->red_disk_count;
2328     else
2329       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330         stored_player[player_nr].inventory_size;
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340   {
2341     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342       get_inventory_element_from_pos(local_player, i);
2343     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344       get_inventory_element_from_pos(local_player, -i - 1);
2345   }
2346
2347   game_panel_controls[GAME_PANEL_SCORE].value = score;
2348   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349
2350   game_panel_controls[GAME_PANEL_TIME].value = time;
2351
2352   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355
2356   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360      EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362     local_player->shield_normal_time_left;
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365      EL_EMPTY);
2366   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367     local_player->shield_deadly_time_left;
2368
2369   game_panel_controls[GAME_PANEL_EXIT].value =
2370     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376      EL_EMC_MAGIC_BALL_SWITCH);
2377
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381     game.light_time_left;
2382
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386     game.timegate_time_left;
2387
2388   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390
2391   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394     game.lenses_time_left;
2395
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399     game.magnify_time_left;
2400
2401   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2403      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2405      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2406      EL_BALLOON_SWITCH_NONE);
2407
2408   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409     local_player->dynabomb_count;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411     local_player->dynabomb_size;
2412   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414
2415   game_panel_controls[GAME_PANEL_PENGUINS].value =
2416     local_player->friends_still_needed;
2417
2418   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419     local_player->sokobanfields_still_needed;
2420   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421     local_player->sokobanfields_still_needed;
2422
2423   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425
2426   for (i = 0; i < NUM_BELTS; i++)
2427   {
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433   }
2434
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438     game.magic_wall_time_left;
2439
2440 #if USE_PLAYER_GRAVITY
2441   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442     local_player->gravity;
2443 #else
2444   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446
2447   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449
2450   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453        game.panel.element[i].id : EL_UNDEFINED);
2454
2455   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458        element_info[game.panel.element_count[i].id].element_count : 0);
2459
2460   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463        element_info[game.panel.ce_score[i].id].collect_score : 0);
2464
2465   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468        element_info[game.panel.ce_score_element[i].id].collect_score :
2469        EL_UNDEFINED);
2470
2471   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474
2475   /* update game panel control frames */
2476
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481     if (gpc->type == TYPE_ELEMENT)
2482     {
2483       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484       {
2485         int last_anim_random_frame = gfx.anim_random_frame;
2486         int element = gpc->value;
2487         int graphic = el2panelimg(element);
2488
2489         if (gpc->value != gpc->last_value)
2490         {
2491           gpc->gfx_frame = 0;
2492           gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494         else
2495         {
2496           gpc->gfx_frame++;
2497
2498           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500             gpc->gfx_random = INIT_GFX_RANDOM();
2501         }
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = gpc->gfx_random;
2505
2506         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507           gpc->gfx_frame = element_info[element].collect_score;
2508
2509         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510                                               gpc->gfx_frame);
2511
2512         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513           gfx.anim_random_frame = last_anim_random_frame;
2514       }
2515     }
2516   }
2517 }
2518
2519 void DisplayGameControlValues()
2520 {
2521   boolean redraw_panel = FALSE;
2522   int i;
2523
2524   for (i = 0; game_panel_controls[i].nr != -1; i++)
2525   {
2526     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527
2528     if (PANEL_DEACTIVATED(gpc->pos))
2529       continue;
2530
2531     if (gpc->value == gpc->last_value &&
2532         gpc->frame == gpc->last_frame)
2533       continue;
2534
2535     redraw_panel = TRUE;
2536   }
2537
2538   if (!redraw_panel)
2539     return;
2540
2541   /* copy default game door content to main double buffer */
2542 #if 1
2543   /* !!! CHECK AGAIN !!! */
2544   SetPanelBackground();
2545   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2547 #else
2548   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2550 #endif
2551
2552   /* redraw game control buttons */
2553 #if 1
2554   RedrawGameButtons();
2555 #else
2556   UnmapGameButtons();
2557   MapGameButtons();
2558 #endif
2559
2560   game_status = GAME_MODE_PSEUDO_PANEL;
2561
2562 #if 1
2563   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2564 #else
2565   for (i = 0; game_panel_controls[i].nr != -1; i++)
2566 #endif
2567   {
2568 #if 1
2569     int nr = game_panel_order[i].nr;
2570     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2571 #else
2572     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2573     int nr = gpc->nr;
2574 #endif
2575     struct TextPosInfo *pos = gpc->pos;
2576     int type = gpc->type;
2577     int value = gpc->value;
2578     int frame = gpc->frame;
2579 #if 0
2580     int last_value = gpc->last_value;
2581     int last_frame = gpc->last_frame;
2582 #endif
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591 #if 0
2592     if (value == last_value && frame == last_frame)
2593       continue;
2594 #endif
2595
2596     gpc->last_value = value;
2597     gpc->last_frame = frame;
2598
2599 #if 0
2600     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2601 #endif
2602
2603     if (type == TYPE_INTEGER)
2604     {
2605       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606           nr == GAME_PANEL_TIME)
2607       {
2608         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2609
2610         if (use_dynamic_size)           /* use dynamic number of digits */
2611         {
2612           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614           int size2 = size1 + 1;
2615           int font1 = pos->font;
2616           int font2 = pos->font_alt;
2617
2618           size = (value < value_change ? size1 : size2);
2619           font = (value < value_change ? font1 : font2);
2620
2621 #if 0
2622           /* clear background if value just changed its size (dynamic digits) */
2623           if ((last_value < value_change) != (value < value_change))
2624           {
2625             int width1 = size1 * getFontWidth(font1);
2626             int width2 = size2 * getFontWidth(font2);
2627             int max_width = MAX(width1, width2);
2628             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2629
2630             pos->width = max_width;
2631
2632             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633                                        max_width, max_height);
2634           }
2635 #endif
2636         }
2637       }
2638
2639 #if 1
2640       /* correct text size if "digits" is zero or less */
2641       if (size <= 0)
2642         size = strlen(int2str(value, size));
2643
2644       /* dynamically correct text alignment */
2645       pos->width = size * getFontWidth(font);
2646 #endif
2647
2648       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649                   int2str(value, size), font, mask_mode);
2650     }
2651     else if (type == TYPE_ELEMENT)
2652     {
2653       int element, graphic;
2654       Bitmap *src_bitmap;
2655       int src_x, src_y;
2656       int width, height;
2657       int dst_x = PANEL_XPOS(pos);
2658       int dst_y = PANEL_YPOS(pos);
2659
2660 #if 1
2661       if (value != EL_UNDEFINED && value != EL_EMPTY)
2662       {
2663         element = value;
2664         graphic = el2panelimg(value);
2665
2666         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2667
2668 #if 1
2669         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2670           size = TILESIZE;
2671 #endif
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2674                               &src_x, &src_y);
2675
2676         width  = graphic_info[graphic].width  * size / TILESIZE;
2677         height = graphic_info[graphic].height * size / TILESIZE;
2678
2679         if (draw_masked)
2680         {
2681           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682                         dst_x - src_x, dst_y - src_y);
2683           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2684                            dst_x, dst_y);
2685         }
2686         else
2687         {
2688           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2689                      dst_x, dst_y);
2690         }
2691       }
2692 #else
2693       if (value == EL_UNDEFINED || value == EL_EMPTY)
2694       {
2695         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696         graphic = el2panelimg(element);
2697
2698         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2701       }
2702       else
2703       {
2704         element = value;
2705         graphic = el2panelimg(value);
2706
2707         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2708       }
2709
2710       width  = graphic_info[graphic].width  * size / TILESIZE;
2711       height = graphic_info[graphic].height * size / TILESIZE;
2712
2713       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2714 #endif
2715     }
2716     else if (type == TYPE_STRING)
2717     {
2718       boolean active = (value != 0);
2719       char *state_normal = "off";
2720       char *state_active = "on";
2721       char *state = (active ? state_active : state_normal);
2722       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2724                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2725                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2726
2727       if (nr == GAME_PANEL_GRAVITY_STATE)
2728       {
2729         int font1 = pos->font;          /* (used for normal state) */
2730         int font2 = pos->font_alt;      /* (used for active state) */
2731 #if 0
2732         int size1 = strlen(state_normal);
2733         int size2 = strlen(state_active);
2734         int width1 = size1 * getFontWidth(font1);
2735         int width2 = size2 * getFontWidth(font2);
2736         int max_width = MAX(width1, width2);
2737         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2738
2739         pos->width = max_width;
2740
2741         /* clear background for values that may have changed its size */
2742         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743                                    max_width, max_height);
2744 #endif
2745
2746         font = (active ? font2 : font1);
2747       }
2748
2749       if (s != NULL)
2750       {
2751         char *s_cut;
2752
2753 #if 1
2754         if (size <= 0)
2755         {
2756           /* don't truncate output if "chars" is zero or less */
2757           size = strlen(s);
2758
2759           /* dynamically correct text alignment */
2760           pos->width = size * getFontWidth(font);
2761         }
2762 #endif
2763
2764         s_cut = getStringCopyN(s, size);
2765
2766         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                     s_cut, font, mask_mode);
2768
2769         free(s_cut);
2770       }
2771     }
2772
2773     redraw_mask |= REDRAW_DOOR_1;
2774   }
2775
2776   game_status = GAME_MODE_PLAYING;
2777 }
2778
2779 void UpdateAndDisplayGameControlValues()
2780 {
2781   if (tape.warp_forward)
2782     return;
2783
2784   UpdateGameControlValues();
2785   DisplayGameControlValues();
2786 }
2787
2788 void DrawGameValue_Emeralds(int value)
2789 {
2790   struct TextPosInfo *pos = &game.panel.gems;
2791 #if 1
2792   int font_nr = pos->font;
2793 #else
2794   int font_nr = FONT_TEXT_2;
2795 #endif
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Dynamite(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.inventory_count;
2814 #if 1
2815   int font_nr = pos->font;
2816 #else
2817   int font_nr = FONT_TEXT_2;
2818 #endif
2819   int font_width = getFontWidth(font_nr);
2820   int chars = pos->size;
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   pos->width = chars * font_width;
2830
2831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2832 }
2833
2834 void DrawGameValue_Score(int value)
2835 {
2836   struct TextPosInfo *pos = &game.panel.score;
2837 #if 1
2838   int font_nr = pos->font;
2839 #else
2840   int font_nr = FONT_TEXT_2;
2841 #endif
2842   int font_width = getFontWidth(font_nr);
2843   int chars = pos->size;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849   if (PANEL_DEACTIVATED(pos))
2850     return;
2851
2852   pos->width = chars * font_width;
2853
2854   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2855 }
2856
2857 void DrawGameValue_Time(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.time;
2860   static int last_value = -1;
2861   int chars1 = 3;
2862   int chars2 = 4;
2863   int chars = pos->size;
2864 #if 1
2865   int font1_nr = pos->font;
2866   int font2_nr = pos->font_alt;
2867 #else
2868   int font1_nr = FONT_TEXT_2;
2869   int font2_nr = FONT_TEXT_1;
2870 #endif
2871   int font_nr = font1_nr;
2872   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2873
2874 #if 1
2875   return;       /* !!! USE NEW STUFF !!! */
2876 #endif
2877
2878   if (PANEL_DEACTIVATED(pos))
2879     return;
2880
2881   if (use_dynamic_chars)                /* use dynamic number of chars */
2882   {
2883     chars   = (value < 1000 ? chars1   : chars2);
2884     font_nr = (value < 1000 ? font1_nr : font2_nr);
2885   }
2886
2887   /* clear background if value just changed its size (dynamic chars only) */
2888   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2889   {
2890     int width1 = chars1 * getFontWidth(font1_nr);
2891     int width2 = chars2 * getFontWidth(font2_nr);
2892     int max_width = MAX(width1, width2);
2893     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2894
2895     pos->width = max_width;
2896
2897     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898                                max_width, max_height);
2899   }
2900
2901   pos->width = chars * getFontWidth(font_nr);
2902
2903   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2904
2905   last_value = value;
2906 }
2907
2908 void DrawGameValue_Level(int value)
2909 {
2910   struct TextPosInfo *pos = &game.panel.level_number;
2911   int chars1 = 2;
2912   int chars2 = 3;
2913   int chars = pos->size;
2914 #if 1
2915   int font1_nr = pos->font;
2916   int font2_nr = pos->font_alt;
2917 #else
2918   int font1_nr = FONT_TEXT_2;
2919   int font2_nr = FONT_TEXT_1;
2920 #endif
2921   int font_nr = font1_nr;
2922   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2923
2924 #if 1
2925   return;       /* !!! USE NEW STUFF !!! */
2926 #endif
2927
2928   if (PANEL_DEACTIVATED(pos))
2929     return;
2930
2931   if (use_dynamic_chars)                /* use dynamic number of chars */
2932   {
2933     chars   = (level_nr < 100 ? chars1   : chars2);
2934     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2935   }
2936
2937   pos->width = chars * getFontWidth(font_nr);
2938
2939   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2940 }
2941
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2943 {
2944 #if 0
2945   struct TextPosInfo *pos = &game.panel.keys;
2946 #endif
2947 #if 0
2948   int base_key_graphic = EL_KEY_1;
2949 #endif
2950   int i;
2951
2952 #if 1
2953   return;       /* !!! USE NEW STUFF !!! */
2954 #endif
2955
2956 #if 0
2957   if (PANEL_DEACTIVATED(pos))
2958     return;
2959 #endif
2960
2961 #if 0
2962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963     base_key_graphic = EL_EM_KEY_1;
2964 #endif
2965
2966 #if 0
2967   pos->width = 4 * MINI_TILEX;
2968 #endif
2969
2970 #if 1
2971   for (i = 0; i < MAX_NUM_KEYS; i++)
2972 #else
2973   /* currently only 4 of 8 possible keys are displayed */
2974   for (i = 0; i < STD_NUM_KEYS; i++)
2975 #endif
2976   {
2977 #if 1
2978     struct TextPosInfo *pos = &game.panel.key[i];
2979 #endif
2980     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981     int src_y = DOOR_GFX_PAGEY1 + 123;
2982 #if 1
2983     int dst_x = PANEL_XPOS(pos);
2984     int dst_y = PANEL_YPOS(pos);
2985 #else
2986     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987     int dst_y = PANEL_YPOS(pos);
2988 #endif
2989
2990 #if 1
2991     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2993                    EL_KEY_1) + i;
2994     int graphic = el2edimg(element);
2995 #endif
2996
2997 #if 1
2998     if (PANEL_DEACTIVATED(pos))
2999       continue;
3000 #endif
3001
3002 #if 0
3003     /* masked blit with tiles from half-size scaled bitmap does not work yet
3004        (no mask bitmap created for these sizes after loading and scaling) --
3005        solution: load without creating mask, scale, then create final mask */
3006
3007     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3009
3010     if (key[i])
3011     {
3012 #if 0
3013       int graphic = el2edimg(base_key_graphic + i);
3014 #endif
3015       Bitmap *src_bitmap;
3016       int src_x, src_y;
3017
3018       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3019
3020       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021                     dst_x - src_x, dst_y - src_y);
3022       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3023                        dst_x, dst_y);
3024     }
3025 #else
3026 #if 1
3027     if (key[i])
3028       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3029     else
3030       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3032 #else
3033     if (key[i])
3034       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3035     else
3036       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3038 #endif
3039 #endif
3040   }
3041 }
3042
3043 #else
3044
3045 void DrawGameValue_Emeralds(int value)
3046 {
3047   int font_nr = FONT_TEXT_2;
3048   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3049
3050   if (PANEL_DEACTIVATED(game.panel.gems))
3051     return;
3052
3053   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3054 }
3055
3056 void DrawGameValue_Dynamite(int value)
3057 {
3058   int font_nr = FONT_TEXT_2;
3059   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060
3061   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3062     return;
3063
3064   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3065 }
3066
3067 void DrawGameValue_Score(int value)
3068 {
3069   int font_nr = FONT_TEXT_2;
3070   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3071
3072   if (PANEL_DEACTIVATED(game.panel.score))
3073     return;
3074
3075   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3076 }
3077
3078 void DrawGameValue_Time(int value)
3079 {
3080   int font1_nr = FONT_TEXT_2;
3081 #if 1
3082   int font2_nr = FONT_TEXT_1;
3083 #else
3084   int font2_nr = FONT_LEVEL_NUMBER;
3085 #endif
3086   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3088
3089   if (PANEL_DEACTIVATED(game.panel.time))
3090     return;
3091
3092   /* clear background if value just changed its size */
3093   if (value == 999 || value == 1000)
3094     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3095
3096   if (value < 1000)
3097     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3098   else
3099     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3100 }
3101
3102 void DrawGameValue_Level(int value)
3103 {
3104   int font1_nr = FONT_TEXT_2;
3105 #if 1
3106   int font2_nr = FONT_TEXT_1;
3107 #else
3108   int font2_nr = FONT_LEVEL_NUMBER;
3109 #endif
3110
3111   if (PANEL_DEACTIVATED(game.panel.level))
3112     return;
3113
3114   if (level_nr < 100)
3115     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3116   else
3117     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3118 }
3119
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3121 {
3122   int base_key_graphic = EL_KEY_1;
3123   int i;
3124
3125   if (PANEL_DEACTIVATED(game.panel.keys))
3126     return;
3127
3128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129     base_key_graphic = EL_EM_KEY_1;
3130
3131   /* currently only 4 of 8 possible keys are displayed */
3132   for (i = 0; i < STD_NUM_KEYS; i++)
3133   {
3134     int x = XX_KEYS + i * MINI_TILEX;
3135     int y = YY_KEYS;
3136
3137     if (key[i])
3138       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3139     else
3140       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3142   }
3143 }
3144
3145 #endif
3146
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3148                        int key_bits)
3149 {
3150   int key[MAX_NUM_KEYS];
3151   int i;
3152
3153   /* prevent EM engine from updating time/score values parallel to GameWon() */
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155       local_player->LevelSolved)
3156     return;
3157
3158   for (i = 0; i < MAX_NUM_KEYS; i++)
3159     key[i] = key_bits & (1 << i);
3160
3161   DrawGameValue_Level(level_nr);
3162
3163   DrawGameValue_Emeralds(emeralds);
3164   DrawGameValue_Dynamite(dynamite);
3165   DrawGameValue_Score(score);
3166   DrawGameValue_Time(time);
3167
3168   DrawGameValue_Keys(key);
3169 }
3170
3171 void UpdateGameDoorValues()
3172 {
3173   UpdateGameControlValues();
3174 }
3175
3176 void DrawGameDoorValues()
3177 {
3178   DisplayGameControlValues();
3179 }
3180
3181 void DrawGameDoorValues_OLD()
3182 {
3183   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184   int dynamite_value = 0;
3185   int score_value = (local_player->LevelSolved ? local_player->score_final :
3186                      local_player->score);
3187   int gems_value = local_player->gems_still_needed;
3188   int key_bits = 0;
3189   int i, j;
3190
3191   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3192   {
3193     DrawGameDoorValues_EM();
3194
3195     return;
3196   }
3197
3198   if (game.centered_player_nr == -1)
3199   {
3200     for (i = 0; i < MAX_PLAYERS; i++)
3201     {
3202       for (j = 0; j < MAX_NUM_KEYS; j++)
3203         if (stored_player[i].key[j])
3204           key_bits |= (1 << j);
3205
3206       dynamite_value += stored_player[i].inventory_size;
3207     }
3208   }
3209   else
3210   {
3211     int player_nr = game.centered_player_nr;
3212
3213     for (i = 0; i < MAX_NUM_KEYS; i++)
3214       if (stored_player[player_nr].key[i])
3215         key_bits |= (1 << i);
3216
3217     dynamite_value = stored_player[player_nr].inventory_size;
3218   }
3219
3220   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3221                     key_bits);
3222 }
3223
3224
3225 /*
3226   =============================================================================
3227   InitGameEngine()
3228   -----------------------------------------------------------------------------
3229   initialize game engine due to level / tape version number
3230   =============================================================================
3231 */
3232
3233 static void InitGameEngine()
3234 {
3235   int i, j, k, l, x, y;
3236
3237   /* set game engine from tape file when re-playing, else from level file */
3238   game.engine_version = (tape.playing ? tape.engine_version :
3239                          level.game_version);
3240
3241   /* ---------------------------------------------------------------------- */
3242   /* set flags for bugs and changes according to active game engine version */
3243   /* ---------------------------------------------------------------------- */
3244
3245   /*
3246     Summary of bugfix/change:
3247     Fixed handling for custom elements that change when pushed by the player.
3248
3249     Fixed/changed in version:
3250     3.1.0
3251
3252     Description:
3253     Before 3.1.0, custom elements that "change when pushing" changed directly
3254     after the player started pushing them (until then handled in "DigField()").
3255     Since 3.1.0, these custom elements are not changed until the "pushing"
3256     move of the element is finished (now handled in "ContinueMoving()").
3257
3258     Affected levels/tapes:
3259     The first condition is generally needed for all levels/tapes before version
3260     3.1.0, which might use the old behaviour before it was changed; known tapes
3261     that are affected are some tapes from the level set "Walpurgis Gardens" by
3262     Jamie Cullen.
3263     The second condition is an exception from the above case and is needed for
3264     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265     above (including some development versions of 3.1.0), but before it was
3266     known that this change would break tapes like the above and was fixed in
3267     3.1.1, so that the changed behaviour was active although the engine version
3268     while recording maybe was before 3.1.0. There is at least one tape that is
3269     affected by this exception, which is the tape for the one-level set "Bug
3270     Machine" by Juergen Bonhagen.
3271   */
3272
3273   game.use_change_when_pushing_bug =
3274     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3275      !(tape.playing &&
3276        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3278
3279   /*
3280     Summary of bugfix/change:
3281     Fixed handling for blocking the field the player leaves when moving.
3282
3283     Fixed/changed in version:
3284     3.1.1
3285
3286     Description:
3287     Before 3.1.1, when "block last field when moving" was enabled, the field
3288     the player is leaving when moving was blocked for the time of the move,
3289     and was directly unblocked afterwards. This resulted in the last field
3290     being blocked for exactly one less than the number of frames of one player
3291     move. Additionally, even when blocking was disabled, the last field was
3292     blocked for exactly one frame.
3293     Since 3.1.1, due to changes in player movement handling, the last field
3294     is not blocked at all when blocking is disabled. When blocking is enabled,
3295     the last field is blocked for exactly the number of frames of one player
3296     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297     last field is blocked for exactly one more than the number of frames of
3298     one player move.
3299
3300     Affected levels/tapes:
3301     (!!! yet to be determined -- probably many !!!)
3302   */
3303
3304   game.use_block_last_field_bug =
3305     (game.engine_version < VERSION_IDENT(3,1,1,0));
3306
3307   /*
3308     Summary of bugfix/change:
3309     Changed behaviour of CE changes with multiple changes per single frame.
3310
3311     Fixed/changed in version:
3312     3.2.0-6
3313
3314     Description:
3315     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316     This resulted in race conditions where CEs seem to behave strange in some
3317     situations (where triggered CE changes were just skipped because there was
3318     already a CE change on that tile in the playfield in that engine frame).
3319     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320     (The number of changes per frame must be limited in any case, because else
3321     it is easily possible to define CE changes that would result in an infinite
3322     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323     should be set large enough so that it would only be reached in cases where
3324     the corresponding CE change conditions run into a loop. Therefore, it seems
3325     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326     maximal number of change pages for custom elements.)
3327
3328     Affected levels/tapes:
3329     Probably many.
3330   */
3331
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333   game.max_num_changes_per_frame = 1;
3334 #else
3335   game.max_num_changes_per_frame =
3336     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3337 #endif
3338
3339   /* ---------------------------------------------------------------------- */
3340
3341   /* default scan direction: scan playfield from top/left to bottom/right */
3342   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3343
3344   /* dynamically adjust element properties according to game engine version */
3345   InitElementPropertiesEngine(game.engine_version);
3346
3347 #if 0
3348   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349   printf("          tape version == %06d [%s] [file: %06d]\n",
3350          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3351          tape.file_version);
3352   printf("       => game.engine_version == %06d\n", game.engine_version);
3353 #endif
3354
3355   /* ---------- initialize player's initial move delay --------------------- */
3356
3357   /* dynamically adjust player properties according to level information */
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359     game.initial_move_delay_value[i] =
3360       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3361
3362   /* dynamically adjust player properties according to game engine version */
3363   for (i = 0; i < MAX_PLAYERS; i++)
3364     game.initial_move_delay[i] =
3365       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366        game.initial_move_delay_value[i] : 0);
3367
3368   /* ---------- initialize player's initial push delay --------------------- */
3369
3370   /* dynamically adjust player properties according to game engine version */
3371   game.initial_push_delay_value =
3372     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3373
3374   /* ---------- initialize changing elements ------------------------------- */
3375
3376   /* initialize changing elements information */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378   {
3379     struct ElementInfo *ei = &element_info[i];
3380
3381     /* this pointer might have been changed in the level editor */
3382     ei->change = &ei->change_page[0];
3383
3384     if (!IS_CUSTOM_ELEMENT(i))
3385     {
3386       ei->change->target_element = EL_EMPTY_SPACE;
3387       ei->change->delay_fixed = 0;
3388       ei->change->delay_random = 0;
3389       ei->change->delay_frames = 1;
3390     }
3391
3392     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3393     {
3394       ei->has_change_event[j] = FALSE;
3395
3396       ei->event_page_nr[j] = 0;
3397       ei->event_page[j] = &ei->change_page[0];
3398     }
3399   }
3400
3401   /* add changing elements from pre-defined list */
3402   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3403   {
3404     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405     struct ElementInfo *ei = &element_info[ch_delay->element];
3406
3407     ei->change->target_element       = ch_delay->target_element;
3408     ei->change->delay_fixed          = ch_delay->change_delay;
3409
3410     ei->change->pre_change_function  = ch_delay->pre_change_function;
3411     ei->change->change_function      = ch_delay->change_function;
3412     ei->change->post_change_function = ch_delay->post_change_function;
3413
3414     ei->change->can_change = TRUE;
3415     ei->change->can_change_or_has_action = TRUE;
3416
3417     ei->has_change_event[CE_DELAY] = TRUE;
3418
3419     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3421   }
3422
3423   /* ---------- initialize internal run-time variables --------------------- */
3424
3425   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3426   {
3427     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3428
3429     for (j = 0; j < ei->num_change_pages; j++)
3430     {
3431       ei->change_page[j].can_change_or_has_action =
3432         (ei->change_page[j].can_change |
3433          ei->change_page[j].has_action);
3434     }
3435   }
3436
3437   /* add change events from custom element configuration */
3438   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3439   {
3440     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3441
3442     for (j = 0; j < ei->num_change_pages; j++)
3443     {
3444       if (!ei->change_page[j].can_change_or_has_action)
3445         continue;
3446
3447       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3448       {
3449         /* only add event page for the first page found with this event */
3450         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3451         {
3452           ei->has_change_event[k] = TRUE;
3453
3454           ei->event_page_nr[k] = j;
3455           ei->event_page[k] = &ei->change_page[j];
3456         }
3457       }
3458     }
3459   }
3460
3461 #if 1
3462   /* ---------- initialize reference elements in change conditions --------- */
3463
3464   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3465   {
3466     int element = EL_CUSTOM_START + i;
3467     struct ElementInfo *ei = &element_info[element];
3468
3469     for (j = 0; j < ei->num_change_pages; j++)
3470     {
3471       int trigger_element = ei->change_page[j].initial_trigger_element;
3472
3473       if (trigger_element >= EL_PREV_CE_8 &&
3474           trigger_element <= EL_NEXT_CE_8)
3475         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3476
3477       ei->change_page[j].trigger_element = trigger_element;
3478     }
3479   }
3480 #endif
3481
3482   /* ---------- initialize run-time trigger player and element ------------- */
3483
3484   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3485   {
3486     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3487
3488     for (j = 0; j < ei->num_change_pages; j++)
3489     {
3490       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494       ei->change_page[j].actual_trigger_ce_value = 0;
3495       ei->change_page[j].actual_trigger_ce_score = 0;
3496     }
3497   }
3498
3499   /* ---------- initialize trigger events ---------------------------------- */
3500
3501   /* initialize trigger events information */
3502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504       trigger_events[i][j] = FALSE;
3505
3506   /* add trigger events from element change event properties */
3507   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3508   {
3509     struct ElementInfo *ei = &element_info[i];
3510
3511     for (j = 0; j < ei->num_change_pages; j++)
3512     {
3513       if (!ei->change_page[j].can_change_or_has_action)
3514         continue;
3515
3516       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3517       {
3518         int trigger_element = ei->change_page[j].trigger_element;
3519
3520         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3521         {
3522           if (ei->change_page[j].has_event[k])
3523           {
3524             if (IS_GROUP_ELEMENT(trigger_element))
3525             {
3526               struct ElementGroupInfo *group =
3527                 element_info[trigger_element].group;
3528
3529               for (l = 0; l < group->num_elements_resolved; l++)
3530                 trigger_events[group->element_resolved[l]][k] = TRUE;
3531             }
3532             else if (trigger_element == EL_ANY_ELEMENT)
3533               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534                 trigger_events[l][k] = TRUE;
3535             else
3536               trigger_events[trigger_element][k] = TRUE;
3537           }
3538         }
3539       }
3540     }
3541   }
3542
3543   /* ---------- initialize push delay -------------------------------------- */
3544
3545   /* initialize push delay values to default */
3546   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3547   {
3548     if (!IS_CUSTOM_ELEMENT(i))
3549     {
3550       /* set default push delay values (corrected since version 3.0.7-1) */
3551       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3552       {
3553         element_info[i].push_delay_fixed = 2;
3554         element_info[i].push_delay_random = 8;
3555       }
3556       else
3557       {
3558         element_info[i].push_delay_fixed = 8;
3559         element_info[i].push_delay_random = 8;
3560       }
3561     }
3562   }
3563
3564   /* set push delay value for certain elements from pre-defined list */
3565   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3566   {
3567     int e = push_delay_list[i].element;
3568
3569     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3570     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3571   }
3572
3573   /* set push delay value for Supaplex elements for newer engine versions */
3574   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3575   {
3576     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577     {
3578       if (IS_SP_ELEMENT(i))
3579       {
3580         /* set SP push delay to just enough to push under a falling zonk */
3581         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3582
3583         element_info[i].push_delay_fixed  = delay;
3584         element_info[i].push_delay_random = 0;
3585       }
3586     }
3587   }
3588
3589   /* ---------- initialize move stepsize ----------------------------------- */
3590
3591   /* initialize move stepsize values to default */
3592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593     if (!IS_CUSTOM_ELEMENT(i))
3594       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3595
3596   /* set move stepsize value for certain elements from pre-defined list */
3597   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3598   {
3599     int e = move_stepsize_list[i].element;
3600
3601     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3602   }
3603
3604   /* ---------- initialize collect score ----------------------------------- */
3605
3606   /* initialize collect score values for custom elements from initial value */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_score = element_info[i].collect_score_initial;
3610
3611   /* ---------- initialize collect count ----------------------------------- */
3612
3613   /* initialize collect count values for non-custom elements */
3614   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615     if (!IS_CUSTOM_ELEMENT(i))
3616       element_info[i].collect_count_initial = 0;
3617
3618   /* add collect count values for all elements from pre-defined list */
3619   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620     element_info[collect_count_list[i].element].collect_count_initial =
3621       collect_count_list[i].count;
3622
3623   /* ---------- initialize access direction -------------------------------- */
3624
3625   /* initialize access direction values to default (access from every side) */
3626   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627     if (!IS_CUSTOM_ELEMENT(i))
3628       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3629
3630   /* set access direction value for certain elements from pre-defined list */
3631   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632     element_info[access_direction_list[i].element].access_direction =
3633       access_direction_list[i].direction;
3634
3635   /* ---------- initialize explosion content ------------------------------- */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637   {
3638     if (IS_CUSTOM_ELEMENT(i))
3639       continue;
3640
3641     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3642     {
3643       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3644
3645       element_info[i].content.e[x][y] =
3646         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648          i == EL_PLAYER_3 ? EL_EMERALD :
3649          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650          i == EL_MOLE ? EL_EMERALD_RED :
3651          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656          i == EL_WALL_EMERALD ? EL_EMERALD :
3657          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662          i == EL_WALL_PEARL ? EL_PEARL :
3663          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3664          EL_EMPTY);
3665     }
3666   }
3667
3668   /* ---------- initialize recursion detection ------------------------------ */
3669   recursion_loop_depth = 0;
3670   recursion_loop_detected = FALSE;
3671   recursion_loop_element = EL_UNDEFINED;
3672
3673   /* ---------- initialize graphics engine ---------------------------------- */
3674   game.scroll_delay_value =
3675     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3677   game.scroll_delay_value =
3678     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3679 }
3680
3681 int get_num_special_action(int element, int action_first, int action_last)
3682 {
3683   int num_special_action = 0;
3684   int i, j;
3685
3686   for (i = action_first; i <= action_last; i++)
3687   {
3688     boolean found = FALSE;
3689
3690     for (j = 0; j < NUM_DIRECTIONS; j++)
3691       if (el_act_dir2img(element, i, j) !=
3692           el_act_dir2img(element, ACTION_DEFAULT, j))
3693         found = TRUE;
3694
3695     if (found)
3696       num_special_action++;
3697     else
3698       break;
3699   }
3700
3701   return num_special_action;
3702 }
3703
3704
3705 /*
3706   =============================================================================
3707   InitGame()
3708   -----------------------------------------------------------------------------
3709   initialize and start new game
3710   =============================================================================
3711 */
3712
3713 void InitGame()
3714 {
3715   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3716   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3717   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3718 #if 0
3719   boolean do_fading = (game_status == GAME_MODE_MAIN);
3720 #endif
3721 #if 1
3722   int initial_move_dir = MV_DOWN;
3723 #else
3724   int initial_move_dir = MV_NONE;
3725 #endif
3726   int i, j, x, y;
3727
3728   game_status = GAME_MODE_PLAYING;
3729
3730 #if 1
3731   /* needed if different viewport properties defined for playing */
3732   ChangeViewportPropertiesIfNeeded();
3733 #endif
3734
3735 #if 1
3736   DrawCompleteVideoDisplay();
3737 #endif
3738
3739   InitGameEngine();
3740   InitGameControlValues();
3741
3742   /* don't play tapes over network */
3743   network_playing = (options.network && !tape.playing);
3744
3745   for (i = 0; i < MAX_PLAYERS; i++)
3746   {
3747     struct PlayerInfo *player = &stored_player[i];
3748
3749     player->index_nr = i;
3750     player->index_bit = (1 << i);
3751     player->element_nr = EL_PLAYER_1 + i;
3752
3753     player->present = FALSE;
3754     player->active = FALSE;
3755     player->mapped = FALSE;
3756
3757     player->killed = FALSE;
3758     player->reanimated = FALSE;
3759
3760     player->action = 0;
3761     player->effective_action = 0;
3762     player->programmed_action = 0;
3763
3764     player->score = 0;
3765     player->score_final = 0;
3766
3767     player->gems_still_needed = level.gems_needed;
3768     player->sokobanfields_still_needed = 0;
3769     player->lights_still_needed = 0;
3770     player->friends_still_needed = 0;
3771
3772     for (j = 0; j < MAX_NUM_KEYS; j++)
3773       player->key[j] = FALSE;
3774
3775     player->num_white_keys = 0;
3776
3777     player->dynabomb_count = 0;
3778     player->dynabomb_size = 1;
3779     player->dynabombs_left = 0;
3780     player->dynabomb_xl = FALSE;
3781
3782     player->MovDir = initial_move_dir;
3783     player->MovPos = 0;
3784     player->GfxPos = 0;
3785     player->GfxDir = initial_move_dir;
3786     player->GfxAction = ACTION_DEFAULT;
3787     player->Frame = 0;
3788     player->StepFrame = 0;
3789
3790     player->initial_element = player->element_nr;
3791     player->artwork_element =
3792       (level.use_artwork_element[i] ? level.artwork_element[i] :
3793        player->element_nr);
3794     player->use_murphy = FALSE;
3795
3796     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3797     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3798
3799     player->gravity = level.initial_player_gravity[i];
3800
3801     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3802
3803     player->actual_frame_counter = 0;
3804
3805     player->step_counter = 0;
3806
3807     player->last_move_dir = initial_move_dir;
3808
3809     player->is_active = FALSE;
3810
3811     player->is_waiting = FALSE;
3812     player->is_moving = FALSE;
3813     player->is_auto_moving = FALSE;
3814     player->is_digging = FALSE;
3815     player->is_snapping = FALSE;
3816     player->is_collecting = FALSE;
3817     player->is_pushing = FALSE;
3818     player->is_switching = FALSE;
3819     player->is_dropping = FALSE;
3820     player->is_dropping_pressed = FALSE;
3821
3822     player->is_bored = FALSE;
3823     player->is_sleeping = FALSE;
3824
3825     player->frame_counter_bored = -1;
3826     player->frame_counter_sleeping = -1;
3827
3828     player->anim_delay_counter = 0;
3829     player->post_delay_counter = 0;
3830
3831     player->dir_waiting = initial_move_dir;
3832     player->action_waiting = ACTION_DEFAULT;
3833     player->last_action_waiting = ACTION_DEFAULT;
3834     player->special_action_bored = ACTION_DEFAULT;
3835     player->special_action_sleeping = ACTION_DEFAULT;
3836
3837     player->switch_x = -1;
3838     player->switch_y = -1;
3839
3840     player->drop_x = -1;
3841     player->drop_y = -1;
3842
3843     player->show_envelope = 0;
3844
3845     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3846
3847     player->push_delay       = -1;      /* initialized when pushing starts */
3848     player->push_delay_value = game.initial_push_delay_value;
3849
3850     player->drop_delay = 0;
3851     player->drop_pressed_delay = 0;
3852
3853     player->last_jx = -1;
3854     player->last_jy = -1;
3855     player->jx = -1;
3856     player->jy = -1;
3857
3858     player->shield_normal_time_left = 0;
3859     player->shield_deadly_time_left = 0;
3860
3861     player->inventory_infinite_element = EL_UNDEFINED;
3862     player->inventory_size = 0;
3863
3864     if (level.use_initial_inventory[i])
3865     {
3866       for (j = 0; j < level.initial_inventory_size[i]; j++)
3867       {
3868         int element = level.initial_inventory_content[i][j];
3869         int collect_count = element_info[element].collect_count_initial;
3870         int k;
3871
3872         if (!IS_CUSTOM_ELEMENT(element))
3873           collect_count = 1;
3874
3875         if (collect_count == 0)
3876           player->inventory_infinite_element = element;
3877         else
3878           for (k = 0; k < collect_count; k++)
3879             if (player->inventory_size < MAX_INVENTORY_SIZE)
3880               player->inventory_element[player->inventory_size++] = element;
3881       }
3882     }
3883
3884     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885     SnapField(player, 0, 0);
3886
3887     player->LevelSolved = FALSE;
3888     player->GameOver = FALSE;
3889
3890     player->LevelSolved_GameWon = FALSE;
3891     player->LevelSolved_GameEnd = FALSE;
3892     player->LevelSolved_PanelOff = FALSE;
3893     player->LevelSolved_SaveTape = FALSE;
3894     player->LevelSolved_SaveScore = FALSE;
3895     player->LevelSolved_CountingTime = 0;
3896     player->LevelSolved_CountingScore = 0;
3897
3898     map_player_action[i] = i;
3899   }
3900
3901   network_player_action_received = FALSE;
3902
3903 #if defined(NETWORK_AVALIABLE)
3904   /* initial null action */
3905   if (network_playing)
3906     SendToServer_MovePlayer(MV_NONE);
3907 #endif
3908
3909   ZX = ZY = -1;
3910   ExitX = ExitY = -1;
3911
3912   FrameCounter = 0;
3913   TimeFrames = 0;
3914   TimePlayed = 0;
3915   TimeLeft = level.time;
3916   TapeTime = 0;
3917
3918   ScreenMovDir = MV_NONE;
3919   ScreenMovPos = 0;
3920   ScreenGfxPos = 0;
3921
3922   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3923
3924   AllPlayersGone = FALSE;
3925
3926   game.yamyam_content_nr = 0;
3927   game.robot_wheel_active = FALSE;
3928   game.magic_wall_active = FALSE;
3929   game.magic_wall_time_left = 0;
3930   game.light_time_left = 0;
3931   game.timegate_time_left = 0;
3932   game.switchgate_pos = 0;
3933   game.wind_direction = level.wind_direction_initial;
3934
3935 #if !USE_PLAYER_GRAVITY
3936   game.gravity = FALSE;
3937   game.explosions_delayed = TRUE;
3938 #endif
3939
3940   game.lenses_time_left = 0;
3941   game.magnify_time_left = 0;
3942
3943   game.ball_state = level.ball_state_initial;
3944   game.ball_content_nr = 0;
3945
3946   game.envelope_active = FALSE;
3947
3948   /* set focus to local player for network games, else to all players */
3949   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950   game.centered_player_nr_next = game.centered_player_nr;
3951   game.set_centered_player = FALSE;
3952
3953   if (network_playing && tape.recording)
3954   {
3955     /* store client dependent player focus when recording network games */
3956     tape.centered_player_nr_next = game.centered_player_nr_next;
3957     tape.set_centered_player = TRUE;
3958   }
3959
3960   for (i = 0; i < NUM_BELTS; i++)
3961   {
3962     game.belt_dir[i] = MV_NONE;
3963     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3964   }
3965
3966   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3968
3969   SCAN_PLAYFIELD(x, y)
3970   {
3971     Feld[x][y] = level.field[x][y];
3972     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973     ChangeDelay[x][y] = 0;
3974     ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976     CustomValue[x][y] = 0;              /* initialized in InitField() */
3977 #endif
3978     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3979     AmoebaNr[x][y] = 0;
3980     WasJustMoving[x][y] = 0;
3981     WasJustFalling[x][y] = 0;
3982     CheckCollision[x][y] = 0;
3983     CheckImpact[x][y] = 0;
3984     Stop[x][y] = FALSE;
3985     Pushed[x][y] = FALSE;
3986
3987     ChangeCount[x][y] = 0;
3988     ChangeEvent[x][y] = -1;
3989
3990     ExplodePhase[x][y] = 0;
3991     ExplodeDelay[x][y] = 0;
3992     ExplodeField[x][y] = EX_TYPE_NONE;
3993
3994     RunnerVisit[x][y] = 0;
3995     PlayerVisit[x][y] = 0;
3996
3997     GfxFrame[x][y] = 0;
3998     GfxRandom[x][y] = INIT_GFX_RANDOM();
3999     GfxElement[x][y] = EL_UNDEFINED;
4000     GfxAction[x][y] = ACTION_DEFAULT;
4001     GfxDir[x][y] = MV_NONE;
4002     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4003   }
4004
4005   SCAN_PLAYFIELD(x, y)
4006   {
4007     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4008       emulate_bd = FALSE;
4009     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4010       emulate_sb = FALSE;
4011     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4012       emulate_sp = FALSE;
4013
4014     InitField(x, y, TRUE);
4015
4016     ResetGfxAnimation(x, y);
4017   }
4018
4019   InitBeltMovement();
4020
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     /* set number of special actions for bored and sleeping animation */
4026     player->num_special_action_bored =
4027       get_num_special_action(player->artwork_element,
4028                              ACTION_BORING_1, ACTION_BORING_LAST);
4029     player->num_special_action_sleeping =
4030       get_num_special_action(player->artwork_element,
4031                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4032   }
4033
4034   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035                     emulate_sb ? EMU_SOKOBAN :
4036                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4037
4038 #if USE_NEW_ALL_SLIPPERY
4039   /* initialize type of slippery elements */
4040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4041   {
4042     if (!IS_CUSTOM_ELEMENT(i))
4043     {
4044       /* default: elements slip down either to the left or right randomly */
4045       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4046
4047       /* SP style elements prefer to slip down on the left side */
4048       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4050
4051       /* BD style elements prefer to slip down on the left side */
4052       if (game.emulation == EMU_BOULDERDASH)
4053         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4054     }
4055   }
4056 #endif
4057
4058   /* initialize explosion and ignition delay */
4059   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4060   {
4061     if (!IS_CUSTOM_ELEMENT(i))
4062     {
4063       int num_phase = 8;
4064       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067       int last_phase = (num_phase + 1) * delay;
4068       int half_phase = (num_phase / 2) * delay;
4069
4070       element_info[i].explosion_delay = last_phase - 1;
4071       element_info[i].ignition_delay = half_phase;
4072
4073       if (i == EL_BLACK_ORB)
4074         element_info[i].ignition_delay = 1;
4075     }
4076
4077 #if 0
4078     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4079       element_info[i].explosion_delay = 1;
4080
4081     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4082       element_info[i].ignition_delay = 1;
4083 #endif
4084   }
4085
4086   /* correct non-moving belts to start moving left */
4087   for (i = 0; i < NUM_BELTS; i++)
4088     if (game.belt_dir[i] == MV_NONE)
4089       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4090
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093   /* choose default local player */
4094   local_player = &stored_player[0];
4095
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097     stored_player[i].connected = FALSE;
4098
4099   local_player->connected = TRUE;
4100   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4101
4102   if (tape.playing)
4103   {
4104     /* try to guess locally connected team mode players (needed for correct
4105        assignment of player figures from level to locally playing players) */
4106
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (tape.player_participates[i])
4109         stored_player[i].connected = TRUE;
4110   }
4111   else if (setup.team_mode && !options.network)
4112   {
4113     /* try to guess locally connected team mode players (needed for correct
4114        assignment of player figures from level to locally playing players) */
4115
4116     for (i = 0; i < MAX_PLAYERS; i++)
4117       if (setup.input[i].use_joystick ||
4118           setup.input[i].key.left != KSYM_UNDEFINED)
4119         stored_player[i].connected = TRUE;
4120   }
4121
4122 #if 0
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124     printf("::: player %d: %s\n", i,
4125            (stored_player[i].connected ? "connected" : "not connected"));
4126
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128     printf("::: player %d: %s\n", i,
4129            (stored_player[i].present ? "present" : "not present"));
4130 #endif
4131
4132   /* check if any connected player was not found in playfield */
4133   for (i = 0; i < MAX_PLAYERS; i++)
4134   {
4135     struct PlayerInfo *player = &stored_player[i];
4136
4137     if (player->connected && !player->present)
4138     {
4139       struct PlayerInfo *field_player = NULL;
4140
4141 #if 0
4142       printf("::: looking for field player for player %d ...\n", i);
4143 #endif
4144
4145       /* assign first free player found that is present in the playfield */
4146
4147       /* first try: look for unmapped playfield player that is not connected */
4148       if (field_player == NULL)
4149         for (j = 0; j < MAX_PLAYERS; j++)
4150           if (stored_player[j].present &&
4151               !stored_player[j].mapped &&
4152               !stored_player[j].connected)
4153             field_player = &stored_player[j];
4154
4155       /* second try: look for *any* unmapped playfield player */
4156       if (field_player == NULL)
4157         for (j = 0; j < MAX_PLAYERS; j++)
4158           if (stored_player[j].present &&
4159               !stored_player[j].mapped)
4160             field_player = &stored_player[j];
4161
4162       if (field_player != NULL)
4163       {
4164         int jx = field_player->jx, jy = field_player->jy;
4165
4166 #if 0
4167         printf("::: found player figure %d\n", field_player->index_nr);
4168 #endif
4169
4170         player->present = FALSE;
4171         player->active = FALSE;
4172
4173         field_player->present = TRUE;
4174         field_player->active = TRUE;
4175
4176         /*
4177         player->initial_element = field_player->initial_element;
4178         player->artwork_element = field_player->artwork_element;
4179
4180         player->block_last_field       = field_player->block_last_field;
4181         player->block_delay_adjustment = field_player->block_delay_adjustment;
4182         */
4183
4184         StorePlayer[jx][jy] = field_player->element_nr;
4185
4186         field_player->jx = field_player->last_jx = jx;
4187         field_player->jy = field_player->last_jy = jy;
4188
4189         if (local_player == player)
4190           local_player = field_player;
4191
4192         map_player_action[field_player->index_nr] = i;
4193
4194         field_player->mapped = TRUE;
4195
4196 #if 0
4197         printf("::: map_player_action[%d] == %d\n",
4198                field_player->index_nr, i);
4199 #endif
4200       }
4201     }
4202
4203     if (player->connected && player->present)
4204       player->mapped = TRUE;
4205   }
4206
4207 #else
4208
4209   /* check if any connected player was not found in playfield */
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211   {
4212     struct PlayerInfo *player = &stored_player[i];
4213
4214     if (player->connected && !player->present)
4215     {
4216       for (j = 0; j < MAX_PLAYERS; j++)
4217       {
4218         struct PlayerInfo *field_player = &stored_player[j];
4219         int jx = field_player->jx, jy = field_player->jy;
4220
4221         /* assign first free player found that is present in the playfield */
4222         if (field_player->present && !field_player->connected)
4223         {
4224           player->present = TRUE;
4225           player->active = TRUE;
4226
4227           field_player->present = FALSE;
4228           field_player->active = FALSE;
4229
4230           player->initial_element = field_player->initial_element;
4231           player->artwork_element = field_player->artwork_element;
4232
4233           player->block_last_field       = field_player->block_last_field;
4234           player->block_delay_adjustment = field_player->block_delay_adjustment;
4235
4236           StorePlayer[jx][jy] = player->element_nr;
4237
4238           player->jx = player->last_jx = jx;
4239           player->jy = player->last_jy = jy;
4240
4241           break;
4242         }
4243       }
4244     }
4245   }
4246 #endif
4247
4248 #if 0
4249   printf("::: local_player->present == %d\n", local_player->present);
4250 #endif
4251
4252   if (tape.playing)
4253   {
4254     /* when playing a tape, eliminate all players who do not participate */
4255
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257     for (i = 0; i < MAX_PLAYERS; i++)
4258     {
4259       if (stored_player[i].active &&
4260           !tape.player_participates[map_player_action[i]])
4261       {
4262         struct PlayerInfo *player = &stored_player[i];
4263         int jx = player->jx, jy = player->jy;
4264
4265         player->active = FALSE;
4266         StorePlayer[jx][jy] = 0;
4267         Feld[jx][jy] = EL_EMPTY;
4268       }
4269     }
4270 #else
4271     for (i = 0; i < MAX_PLAYERS; i++)
4272     {
4273       if (stored_player[i].active &&
4274           !tape.player_participates[i])
4275       {
4276         struct PlayerInfo *player = &stored_player[i];
4277         int jx = player->jx, jy = player->jy;
4278
4279         player->active = FALSE;
4280         StorePlayer[jx][jy] = 0;
4281         Feld[jx][jy] = EL_EMPTY;
4282       }
4283     }
4284 #endif
4285   }
4286   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4287   {
4288     /* when in single player mode, eliminate all but the first active player */
4289
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291     {
4292       if (stored_player[i].active)
4293       {
4294         for (j = i + 1; j < MAX_PLAYERS; j++)
4295         {
4296           if (stored_player[j].active)
4297           {
4298             struct PlayerInfo *player = &stored_player[j];
4299             int jx = player->jx, jy = player->jy;
4300
4301             player->active = FALSE;
4302             player->present = FALSE;
4303
4304             StorePlayer[jx][jy] = 0;
4305             Feld[jx][jy] = EL_EMPTY;
4306           }
4307         }
4308       }
4309     }
4310   }
4311
4312   /* when recording the game, store which players take part in the game */
4313   if (tape.recording)
4314   {
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316     for (i = 0; i < MAX_PLAYERS; i++)
4317       if (stored_player[i].connected)
4318         tape.player_participates[i] = TRUE;
4319 #else
4320     for (i = 0; i < MAX_PLAYERS; i++)
4321       if (stored_player[i].active)
4322         tape.player_participates[i] = TRUE;
4323 #endif
4324   }
4325
4326   if (options.debug)
4327   {
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329     {
4330       struct PlayerInfo *player = &stored_player[i];
4331
4332       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4333              i+1,
4334              player->present,
4335              player->connected,
4336              player->active);
4337       if (local_player == player)
4338         printf("Player  %d is local player.\n", i+1);
4339     }
4340   }
4341
4342   if (BorderElement == EL_EMPTY)
4343   {
4344     SBX_Left = 0;
4345     SBX_Right = lev_fieldx - SCR_FIELDX;
4346     SBY_Upper = 0;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY;
4348   }
4349   else
4350   {
4351     SBX_Left = -1;
4352     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4353     SBY_Upper = -1;
4354     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4355   }
4356
4357 #if NEW_TILESIZE
4358
4359   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4360     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4361
4362   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4363     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4364
4365   if (EVEN(SCR_FIELDX))
4366     SBX_Left--;
4367   if (EVEN(SCR_FIELDY))
4368     SBY_Upper--;
4369
4370 #else
4371
4372   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4373     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4374
4375   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4376     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4377 #endif
4378
4379   /* if local player not found, look for custom element that might create
4380      the player (make some assumptions about the right custom element) */
4381   if (!local_player->present)
4382   {
4383     int start_x = 0, start_y = 0;
4384     int found_rating = 0;
4385     int found_element = EL_UNDEFINED;
4386     int player_nr = local_player->index_nr;
4387
4388     SCAN_PLAYFIELD(x, y)
4389     {
4390       int element = Feld[x][y];
4391       int content;
4392       int xx, yy;
4393       boolean is_player;
4394
4395       if (level.use_start_element[player_nr] &&
4396           level.start_element[player_nr] == element &&
4397           found_rating < 4)
4398       {
4399         start_x = x;
4400         start_y = y;
4401
4402         found_rating = 4;
4403         found_element = element;
4404       }
4405
4406       if (!IS_CUSTOM_ELEMENT(element))
4407         continue;
4408
4409       if (CAN_CHANGE(element))
4410       {
4411         for (i = 0; i < element_info[element].num_change_pages; i++)
4412         {
4413           /* check for player created from custom element as single target */
4414           content = element_info[element].change_page[i].target_element;
4415           is_player = ELEM_IS_PLAYER(content);
4416
4417           if (is_player && (found_rating < 3 ||
4418                             (found_rating == 3 && element < found_element)))
4419           {
4420             start_x = x;
4421             start_y = y;
4422
4423             found_rating = 3;
4424             found_element = element;
4425           }
4426         }
4427       }
4428
4429       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4430       {
4431         /* check for player created from custom element as explosion content */
4432         content = element_info[element].content.e[xx][yy];
4433         is_player = ELEM_IS_PLAYER(content);
4434
4435         if (is_player && (found_rating < 2 ||
4436                           (found_rating == 2 && element < found_element)))
4437         {
4438           start_x = x + xx - 1;
4439           start_y = y + yy - 1;
4440
4441           found_rating = 2;
4442           found_element = element;
4443         }
4444
4445         if (!CAN_CHANGE(element))
4446           continue;
4447
4448         for (i = 0; i < element_info[element].num_change_pages; i++)
4449         {
4450           /* check for player created from custom element as extended target */
4451           content =
4452             element_info[element].change_page[i].target_content.e[xx][yy];
4453
4454           is_player = ELEM_IS_PLAYER(content);
4455
4456           if (is_player && (found_rating < 1 ||
4457                             (found_rating == 1 && element < found_element)))
4458           {
4459             start_x = x + xx - 1;
4460             start_y = y + yy - 1;
4461
4462             found_rating = 1;
4463             found_element = element;
4464           }
4465         }
4466       }
4467     }
4468
4469     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4470                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4471                 start_x - MIDPOSX);
4472
4473     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4474                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4475                 start_y - MIDPOSY);
4476   }
4477   else
4478   {
4479     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4480                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4481                 local_player->jx - MIDPOSX);
4482
4483     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4484                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4485                 local_player->jy - MIDPOSY);
4486   }
4487
4488   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4489
4490 #if 0
4491   /* do not use PLAYING mask for fading out from main screen */
4492   game_status = GAME_MODE_MAIN;
4493 #endif
4494
4495   StopAnimation();
4496
4497   if (!game.restart_level)
4498     CloseDoor(DOOR_CLOSE_1);
4499
4500 #if 1
4501   if (level_editor_test_game)
4502     FadeSkipNextFadeIn();
4503   else
4504     FadeSetEnterScreen();
4505 #else
4506   if (level_editor_test_game)
4507     fading = fading_none;
4508   else
4509     fading = menu.destination;
4510 #endif
4511
4512 #if 1
4513   FadeOut(REDRAW_FIELD);
4514 #else
4515   if (do_fading)
4516     FadeOut(REDRAW_FIELD);
4517 #endif
4518
4519 #if 0
4520   game_status = GAME_MODE_PLAYING;
4521 #endif
4522
4523   /* !!! FIX THIS (START) !!! */
4524   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4525   {
4526     InitGameEngine_EM();
4527
4528     /* blit playfield from scroll buffer to normal back buffer for fading in */
4529     BlitScreenToBitmap_EM(backbuffer);
4530   }
4531   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4532   {
4533     InitGameEngine_SP();
4534
4535     /* blit playfield from scroll buffer to normal back buffer for fading in */
4536     BlitScreenToBitmap_SP(backbuffer);
4537   }
4538   else
4539   {
4540     DrawLevel();
4541     DrawAllPlayers();
4542
4543     /* after drawing the level, correct some elements */
4544     if (game.timegate_time_left == 0)
4545       CloseAllOpenTimegates();
4546
4547 #if NEW_TILESIZE
4548     BlitScreenToBitmap(backbuffer);
4549 #else
4550     /* blit playfield from scroll buffer to normal back buffer for fading in */
4551     if (setup.soft_scrolling)
4552       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4553 #endif
4554
4555     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4556   }
4557   /* !!! FIX THIS (END) !!! */
4558
4559 #if 1
4560   FadeIn(REDRAW_FIELD);
4561 #else
4562   if (do_fading)
4563     FadeIn(REDRAW_FIELD);
4564
4565   BackToFront();
4566 #endif
4567
4568   if (!game.restart_level)
4569   {
4570     /* copy default game door content to main double buffer */
4571 #if 1
4572 #if 1
4573     /* !!! CHECK AGAIN !!! */
4574     SetPanelBackground();
4575     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4576     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4577 #else
4578     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4579
4580     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4581     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4582     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4583                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4584 #endif
4585 #else
4586     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4587                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4588 #endif
4589   }
4590
4591   SetPanelBackground();
4592   SetDrawBackgroundMask(REDRAW_DOOR_1);
4593
4594 #if 1
4595   UpdateAndDisplayGameControlValues();
4596 #else
4597   UpdateGameDoorValues();
4598   DrawGameDoorValues();
4599 #endif
4600
4601   if (!game.restart_level)
4602   {
4603     UnmapGameButtons();
4604     UnmapTapeButtons();
4605     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4606     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4607     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4608     MapGameButtons();
4609     MapTapeButtons();
4610
4611     /* copy actual game door content to door double buffer for OpenDoor() */
4612     BlitBitmap(drawto, bitmap_db_door,
4613                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4614
4615     OpenDoor(DOOR_OPEN_ALL);
4616
4617     PlaySound(SND_GAME_STARTING);
4618
4619     if (setup.sound_music)
4620       PlayLevelMusic();
4621
4622     KeyboardAutoRepeatOffUnlessAutoplay();
4623
4624     if (options.debug)
4625     {
4626       for (i = 0; i < MAX_PLAYERS; i++)
4627         printf("Player %d %sactive.\n",
4628                i + 1, (stored_player[i].active ? "" : "not "));
4629     }
4630   }
4631
4632 #if 1
4633   UnmapAllGadgets();
4634
4635   MapGameButtons();
4636   MapTapeButtons();
4637 #endif
4638
4639   game.restart_level = FALSE;
4640 }
4641
4642 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4643 {
4644   /* this is used for non-R'n'D game engines to update certain engine values */
4645
4646   /* needed to determine if sounds are played within the visible screen area */
4647   scroll_x = actual_scroll_x;
4648   scroll_y = actual_scroll_y;
4649 }
4650
4651 void InitMovDir(int x, int y)
4652 {
4653   int i, element = Feld[x][y];
4654   static int xy[4][2] =
4655   {
4656     {  0, +1 },
4657     { +1,  0 },
4658     {  0, -1 },
4659     { -1,  0 }
4660   };
4661   static int direction[3][4] =
4662   {
4663     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4664     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4665     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4666   };
4667
4668   switch (element)
4669   {
4670     case EL_BUG_RIGHT:
4671     case EL_BUG_UP:
4672     case EL_BUG_LEFT:
4673     case EL_BUG_DOWN:
4674       Feld[x][y] = EL_BUG;
4675       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4676       break;
4677
4678     case EL_SPACESHIP_RIGHT:
4679     case EL_SPACESHIP_UP:
4680     case EL_SPACESHIP_LEFT:
4681     case EL_SPACESHIP_DOWN:
4682       Feld[x][y] = EL_SPACESHIP;
4683       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4684       break;
4685
4686     case EL_BD_BUTTERFLY_RIGHT:
4687     case EL_BD_BUTTERFLY_UP:
4688     case EL_BD_BUTTERFLY_LEFT:
4689     case EL_BD_BUTTERFLY_DOWN:
4690       Feld[x][y] = EL_BD_BUTTERFLY;
4691       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4692       break;
4693
4694     case EL_BD_FIREFLY_RIGHT:
4695     case EL_BD_FIREFLY_UP:
4696     case EL_BD_FIREFLY_LEFT:
4697     case EL_BD_FIREFLY_DOWN:
4698       Feld[x][y] = EL_BD_FIREFLY;
4699       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4700       break;
4701
4702     case EL_PACMAN_RIGHT:
4703     case EL_PACMAN_UP:
4704     case EL_PACMAN_LEFT:
4705     case EL_PACMAN_DOWN:
4706       Feld[x][y] = EL_PACMAN;
4707       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4708       break;
4709
4710     case EL_YAMYAM_LEFT:
4711     case EL_YAMYAM_RIGHT:
4712     case EL_YAMYAM_UP:
4713     case EL_YAMYAM_DOWN:
4714       Feld[x][y] = EL_YAMYAM;
4715       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4716       break;
4717
4718     case EL_SP_SNIKSNAK:
4719       MovDir[x][y] = MV_UP;
4720       break;
4721
4722     case EL_SP_ELECTRON:
4723       MovDir[x][y] = MV_LEFT;
4724       break;
4725
4726     case EL_MOLE_LEFT:
4727     case EL_MOLE_RIGHT:
4728     case EL_MOLE_UP:
4729     case EL_MOLE_DOWN:
4730       Feld[x][y] = EL_MOLE;
4731       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4732       break;
4733
4734     default:
4735       if (IS_CUSTOM_ELEMENT(element))
4736       {
4737         struct ElementInfo *ei = &element_info[element];
4738         int move_direction_initial = ei->move_direction_initial;
4739         int move_pattern = ei->move_pattern;
4740
4741         if (move_direction_initial == MV_START_PREVIOUS)
4742         {
4743           if (MovDir[x][y] != MV_NONE)
4744             return;
4745
4746           move_direction_initial = MV_START_AUTOMATIC;
4747         }
4748
4749         if (move_direction_initial == MV_START_RANDOM)
4750           MovDir[x][y] = 1 << RND(4);
4751         else if (move_direction_initial & MV_ANY_DIRECTION)
4752           MovDir[x][y] = move_direction_initial;
4753         else if (move_pattern == MV_ALL_DIRECTIONS ||
4754                  move_pattern == MV_TURNING_LEFT ||
4755                  move_pattern == MV_TURNING_RIGHT ||
4756                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4757                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4758                  move_pattern == MV_TURNING_RANDOM)
4759           MovDir[x][y] = 1 << RND(4);
4760         else if (move_pattern == MV_HORIZONTAL)
4761           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4762         else if (move_pattern == MV_VERTICAL)
4763           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4764         else if (move_pattern & MV_ANY_DIRECTION)
4765           MovDir[x][y] = element_info[element].move_pattern;
4766         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4767                  move_pattern == MV_ALONG_RIGHT_SIDE)
4768         {
4769           /* use random direction as default start direction */
4770           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4771             MovDir[x][y] = 1 << RND(4);
4772
4773           for (i = 0; i < NUM_DIRECTIONS; i++)
4774           {
4775             int x1 = x + xy[i][0];
4776             int y1 = y + xy[i][1];
4777
4778             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4779             {
4780               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4781                 MovDir[x][y] = direction[0][i];
4782               else
4783                 MovDir[x][y] = direction[1][i];
4784
4785               break;
4786             }
4787           }
4788         }                
4789       }
4790       else
4791       {
4792         MovDir[x][y] = 1 << RND(4);
4793
4794         if (element != EL_BUG &&
4795             element != EL_SPACESHIP &&
4796             element != EL_BD_BUTTERFLY &&
4797             element != EL_BD_FIREFLY)
4798           break;
4799
4800         for (i = 0; i < NUM_DIRECTIONS; i++)
4801         {
4802           int x1 = x + xy[i][0];
4803           int y1 = y + xy[i][1];
4804
4805           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4806           {
4807             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4808             {
4809               MovDir[x][y] = direction[0][i];
4810               break;
4811             }
4812             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4813                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4814             {
4815               MovDir[x][y] = direction[1][i];
4816               break;
4817             }
4818           }
4819         }
4820       }
4821       break;
4822   }
4823
4824   GfxDir[x][y] = MovDir[x][y];
4825 }
4826
4827 void InitAmoebaNr(int x, int y)
4828 {
4829   int i;
4830   int group_nr = AmoebeNachbarNr(x, y);
4831
4832   if (group_nr == 0)
4833   {
4834     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4835     {
4836       if (AmoebaCnt[i] == 0)
4837       {
4838         group_nr = i;
4839         break;
4840       }
4841     }
4842   }
4843
4844   AmoebaNr[x][y] = group_nr;
4845   AmoebaCnt[group_nr]++;
4846   AmoebaCnt2[group_nr]++;
4847 }
4848
4849 static void PlayerWins(struct PlayerInfo *player)
4850 {
4851   player->LevelSolved = TRUE;
4852   player->GameOver = TRUE;
4853
4854   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4855                          level.native_em_level->lev->score : player->score);
4856
4857   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4858   player->LevelSolved_CountingScore = player->score_final;
4859 }
4860
4861 void GameWon()
4862 {
4863   static int time, time_final;
4864   static int score, score_final;
4865   static int game_over_delay_1 = 0;
4866   static int game_over_delay_2 = 0;
4867   int game_over_delay_value_1 = 50;
4868   int game_over_delay_value_2 = 50;
4869
4870   if (!local_player->LevelSolved_GameWon)
4871   {
4872     int i;
4873
4874     /* do not start end game actions before the player stops moving (to exit) */
4875     if (local_player->MovPos)
4876       return;
4877
4878     local_player->LevelSolved_GameWon = TRUE;
4879     local_player->LevelSolved_SaveTape = tape.recording;
4880     local_player->LevelSolved_SaveScore = !tape.playing;
4881
4882     if (tape.auto_play)         /* tape might already be stopped here */
4883       tape.auto_play_level_solved = TRUE;
4884
4885 #if 1
4886     TapeStop();
4887 #endif
4888
4889     game_over_delay_1 = game_over_delay_value_1;
4890     game_over_delay_2 = game_over_delay_value_2;
4891
4892     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4893     score = score_final = local_player->score_final;
4894
4895     if (TimeLeft > 0)
4896     {
4897       time_final = 0;
4898       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4899     }
4900     else if (level.time == 0 && TimePlayed < 999)
4901     {
4902       time_final = 999;
4903       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4904     }
4905
4906     local_player->score_final = score_final;
4907
4908     if (level_editor_test_game)
4909     {
4910       time = time_final;
4911       score = score_final;
4912
4913 #if 1
4914       local_player->LevelSolved_CountingTime = time;
4915       local_player->LevelSolved_CountingScore = score;
4916
4917       game_panel_controls[GAME_PANEL_TIME].value = time;
4918       game_panel_controls[GAME_PANEL_SCORE].value = score;
4919
4920       DisplayGameControlValues();
4921 #else
4922       DrawGameValue_Time(time);
4923       DrawGameValue_Score(score);
4924 #endif
4925     }
4926
4927     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4928     {
4929       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4930       {
4931         /* close exit door after last player */
4932         if ((AllPlayersGone &&
4933              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4934               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4935               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4936             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4937             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4938         {
4939           int element = Feld[ExitX][ExitY];
4940
4941 #if 0
4942           if (element == EL_EM_EXIT_OPEN ||
4943               element == EL_EM_STEEL_EXIT_OPEN)
4944           {
4945             Bang(ExitX, ExitY);
4946           }
4947           else
4948 #endif
4949           {
4950             Feld[ExitX][ExitY] =
4951               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4952                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4953                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4954                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4955                EL_EM_STEEL_EXIT_CLOSING);
4956
4957             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4958           }
4959         }
4960
4961         /* player disappears */
4962         DrawLevelField(ExitX, ExitY);
4963       }
4964
4965       for (i = 0; i < MAX_PLAYERS; i++)
4966       {
4967         struct PlayerInfo *player = &stored_player[i];
4968
4969         if (player->present)
4970         {
4971           RemovePlayer(player);
4972
4973           /* player disappears */
4974           DrawLevelField(player->jx, player->jy);
4975         }
4976       }
4977     }
4978
4979     PlaySound(SND_GAME_WINNING);
4980   }
4981
4982   if (game_over_delay_1 > 0)
4983   {
4984     game_over_delay_1--;
4985
4986     return;
4987   }
4988
4989   if (time != time_final)
4990   {
4991     int time_to_go = ABS(time_final - time);
4992     int time_count_dir = (time < time_final ? +1 : -1);
4993     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4994
4995     time  += time_count_steps * time_count_dir;
4996     score += time_count_steps * level.score[SC_TIME_BONUS];
4997
4998 #if 1
4999     local_player->LevelSolved_CountingTime = time;
5000     local_player->LevelSolved_CountingScore = score;
5001
5002     game_panel_controls[GAME_PANEL_TIME].value = time;
5003     game_panel_controls[GAME_PANEL_SCORE].value = score;
5004
5005     DisplayGameControlValues();
5006 #else
5007     DrawGameValue_Time(time);
5008     DrawGameValue_Score(score);
5009 #endif
5010
5011     if (time == time_final)
5012       StopSound(SND_GAME_LEVELTIME_BONUS);
5013     else if (setup.sound_loops)
5014       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5015     else
5016       PlaySound(SND_GAME_LEVELTIME_BONUS);
5017
5018     return;
5019   }
5020
5021   local_player->LevelSolved_PanelOff = TRUE;
5022
5023   if (game_over_delay_2 > 0)
5024   {
5025     game_over_delay_2--;
5026
5027     return;
5028   }
5029
5030 #if 1
5031   GameEnd();
5032 #endif
5033 }
5034
5035 void GameEnd()
5036 {
5037   int hi_pos;
5038   boolean raise_level = FALSE;
5039
5040   local_player->LevelSolved_GameEnd = TRUE;
5041
5042   CloseDoor(DOOR_CLOSE_1);
5043
5044   if (local_player->LevelSolved_SaveTape)
5045   {
5046 #if 0
5047     TapeStop();
5048 #endif
5049
5050 #if 1
5051     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5052 #else
5053     SaveTape(tape.level_nr);            /* ask to save tape */
5054 #endif
5055   }
5056
5057   if (level_editor_test_game)
5058   {
5059     game_status = GAME_MODE_MAIN;
5060
5061 #if 1
5062     DrawAndFadeInMainMenu(REDRAW_FIELD);
5063 #else
5064     DrawMainMenu();
5065 #endif
5066
5067     return;
5068   }
5069
5070   if (!local_player->LevelSolved_SaveScore)
5071   {
5072 #if 1
5073     FadeOut(REDRAW_FIELD);
5074 #endif
5075
5076     game_status = GAME_MODE_MAIN;
5077
5078     DrawAndFadeInMainMenu(REDRAW_FIELD);
5079
5080     return;
5081   }
5082
5083   if (level_nr == leveldir_current->handicap_level)
5084   {
5085     leveldir_current->handicap_level++;
5086     SaveLevelSetup_SeriesInfo();
5087   }
5088
5089   if (level_nr < leveldir_current->last_level)
5090     raise_level = TRUE;                 /* advance to next level */
5091
5092   if ((hi_pos = NewHiScore()) >= 0) 
5093   {
5094     game_status = GAME_MODE_SCORES;
5095
5096     DrawHallOfFame(hi_pos);
5097
5098     if (raise_level)
5099     {
5100       level_nr++;
5101       TapeErase();
5102     }
5103   }
5104   else
5105   {
5106 #if 1
5107     FadeOut(REDRAW_FIELD);
5108 #endif
5109
5110     game_status = GAME_MODE_MAIN;
5111
5112     if (raise_level)
5113     {
5114       level_nr++;
5115       TapeErase();
5116     }
5117
5118     DrawAndFadeInMainMenu(REDRAW_FIELD);
5119   }
5120 }
5121
5122 int NewHiScore()
5123 {
5124   int k, l;
5125   int position = -1;
5126
5127   LoadScore(level_nr);
5128
5129   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5130       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5131     return -1;
5132
5133   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5134   {
5135     if (local_player->score_final > highscore[k].Score)
5136     {
5137       /* player has made it to the hall of fame */
5138
5139       if (k < MAX_SCORE_ENTRIES - 1)
5140       {
5141         int m = MAX_SCORE_ENTRIES - 1;
5142
5143 #ifdef ONE_PER_NAME
5144         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5145           if (strEqual(setup.player_name, highscore[l].Name))
5146             m = l;
5147         if (m == k)     /* player's new highscore overwrites his old one */
5148           goto put_into_list;
5149 #endif
5150
5151         for (l = m; l > k; l--)
5152         {
5153           strcpy(highscore[l].Name, highscore[l - 1].Name);
5154           highscore[l].Score = highscore[l - 1].Score;
5155         }
5156       }
5157
5158 #ifdef ONE_PER_NAME
5159       put_into_list:
5160 #endif
5161       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5162       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5163       highscore[k].Score = local_player->score_final; 
5164       position = k;
5165       break;
5166     }
5167
5168 #ifdef ONE_PER_NAME
5169     else if (!strncmp(setup.player_name, highscore[k].Name,
5170                       MAX_PLAYER_NAME_LEN))
5171       break;    /* player already there with a higher score */
5172 #endif
5173
5174   }
5175
5176   if (position >= 0) 
5177     SaveScore(level_nr);
5178
5179   return position;
5180 }
5181
5182 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5183 {
5184   int element = Feld[x][y];
5185   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5187   int horiz_move = (dx != 0);
5188   int sign = (horiz_move ? dx : dy);
5189   int step = sign * element_info[element].move_stepsize;
5190
5191   /* special values for move stepsize for spring and things on conveyor belt */
5192   if (horiz_move)
5193   {
5194     if (CAN_FALL(element) &&
5195         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5196       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5197     else if (element == EL_SPRING)
5198       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5199   }
5200
5201   return step;
5202 }
5203
5204 inline static int getElementMoveStepsize(int x, int y)
5205 {
5206   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5207 }
5208
5209 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5210 {
5211   if (player->GfxAction != action || player->GfxDir != dir)
5212   {
5213 #if 0
5214     printf("Player frame reset! (%d => %d, %d => %d)\n",
5215            player->GfxAction, action, player->GfxDir, dir);
5216 #endif
5217
5218     player->GfxAction = action;
5219     player->GfxDir = dir;
5220     player->Frame = 0;
5221     player->StepFrame = 0;
5222   }
5223 }
5224
5225 #if USE_GFX_RESET_GFX_ANIMATION
5226 static void ResetGfxFrame(int x, int y, boolean redraw)
5227 {
5228   int element = Feld[x][y];
5229   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5230   int last_gfx_frame = GfxFrame[x][y];
5231
5232   if (graphic_info[graphic].anim_global_sync)
5233     GfxFrame[x][y] = FrameCounter;
5234   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5235     GfxFrame[x][y] = CustomValue[x][y];
5236   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5237     GfxFrame[x][y] = element_info[element].collect_score;
5238   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5239     GfxFrame[x][y] = ChangeDelay[x][y];
5240
5241   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5242     DrawLevelGraphicAnimation(x, y, graphic);
5243 }
5244 #endif
5245
5246 static void ResetGfxAnimation(int x, int y)
5247 {
5248   GfxAction[x][y] = ACTION_DEFAULT;
5249   GfxDir[x][y] = MovDir[x][y];
5250   GfxFrame[x][y] = 0;
5251
5252 #if USE_GFX_RESET_GFX_ANIMATION
5253   ResetGfxFrame(x, y, FALSE);
5254 #endif
5255 }
5256
5257 static void ResetRandomAnimationValue(int x, int y)
5258 {
5259   GfxRandom[x][y] = INIT_GFX_RANDOM();
5260 }
5261
5262 void InitMovingField(int x, int y, int direction)
5263 {
5264   int element = Feld[x][y];
5265   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5266   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5267   int newx = x + dx;
5268   int newy = y + dy;
5269   boolean is_moving_before, is_moving_after;
5270 #if 0
5271   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5272 #endif
5273
5274   /* check if element was/is moving or being moved before/after mode change */
5275 #if 1
5276 #if 1
5277   is_moving_before = (WasJustMoving[x][y] != 0);
5278 #else
5279   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5280   is_moving_before = WasJustMoving[x][y];
5281 #endif
5282 #else
5283   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5284 #endif
5285   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5286
5287   /* reset animation only for moving elements which change direction of moving
5288      or which just started or stopped moving
5289      (else CEs with property "can move" / "not moving" are reset each frame) */
5290 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5291 #if 1
5292   if (is_moving_before != is_moving_after ||
5293       direction != MovDir[x][y])
5294     ResetGfxAnimation(x, y);
5295 #else
5296   if ((is_moving_before || is_moving_after) && !continues_moving)
5297     ResetGfxAnimation(x, y);
5298 #endif
5299 #else
5300   if (!continues_moving)
5301     ResetGfxAnimation(x, y);
5302 #endif
5303
5304   MovDir[x][y] = direction;
5305   GfxDir[x][y] = direction;
5306
5307 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5308   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5309                      direction == MV_DOWN && CAN_FALL(element) ?
5310                      ACTION_FALLING : ACTION_MOVING);
5311 #else
5312   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5313                      ACTION_FALLING : ACTION_MOVING);
5314 #endif
5315
5316   /* this is needed for CEs with property "can move" / "not moving" */
5317
5318   if (is_moving_after)
5319   {
5320     if (Feld[newx][newy] == EL_EMPTY)
5321       Feld[newx][newy] = EL_BLOCKED;
5322
5323     MovDir[newx][newy] = MovDir[x][y];
5324
5325 #if USE_NEW_CUSTOM_VALUE
5326     CustomValue[newx][newy] = CustomValue[x][y];
5327 #endif
5328
5329     GfxFrame[newx][newy] = GfxFrame[x][y];
5330     GfxRandom[newx][newy] = GfxRandom[x][y];
5331     GfxAction[newx][newy] = GfxAction[x][y];
5332     GfxDir[newx][newy] = GfxDir[x][y];
5333   }
5334 }
5335
5336 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5337 {
5338   int direction = MovDir[x][y];
5339   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5340   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5341
5342   *goes_to_x = newx;
5343   *goes_to_y = newy;
5344 }
5345
5346 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5347 {
5348   int oldx = x, oldy = y;
5349   int direction = MovDir[x][y];
5350
5351   if (direction == MV_LEFT)
5352     oldx++;
5353   else if (direction == MV_RIGHT)
5354     oldx--;
5355   else if (direction == MV_UP)
5356     oldy++;
5357   else if (direction == MV_DOWN)
5358     oldy--;
5359
5360   *comes_from_x = oldx;
5361   *comes_from_y = oldy;
5362 }
5363
5364 int MovingOrBlocked2Element(int x, int y)
5365 {
5366   int element = Feld[x][y];
5367
5368   if (element == EL_BLOCKED)
5369   {
5370     int oldx, oldy;
5371
5372     Blocked2Moving(x, y, &oldx, &oldy);
5373     return Feld[oldx][oldy];
5374   }
5375   else
5376     return element;
5377 }
5378
5379 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5380 {
5381   /* like MovingOrBlocked2Element(), but if element is moving
5382      and (x,y) is the field the moving element is just leaving,
5383      return EL_BLOCKED instead of the element value */
5384   int element = Feld[x][y];
5385
5386   if (IS_MOVING(x, y))
5387   {
5388     if (element == EL_BLOCKED)
5389     {
5390       int oldx, oldy;
5391
5392       Blocked2Moving(x, y, &oldx, &oldy);
5393       return Feld[oldx][oldy];
5394     }
5395     else
5396       return EL_BLOCKED;
5397   }
5398   else
5399     return element;
5400 }
5401
5402 static void RemoveField(int x, int y)
5403 {
5404   Feld[x][y] = EL_EMPTY;
5405
5406   MovPos[x][y] = 0;
5407   MovDir[x][y] = 0;
5408   MovDelay[x][y] = 0;
5409
5410 #if USE_NEW_CUSTOM_VALUE
5411   CustomValue[x][y] = 0;
5412 #endif
5413
5414   AmoebaNr[x][y] = 0;
5415   ChangeDelay[x][y] = 0;
5416   ChangePage[x][y] = -1;
5417   Pushed[x][y] = FALSE;
5418
5419 #if 0
5420   ExplodeField[x][y] = EX_TYPE_NONE;
5421 #endif
5422
5423   GfxElement[x][y] = EL_UNDEFINED;
5424   GfxAction[x][y] = ACTION_DEFAULT;
5425   GfxDir[x][y] = MV_NONE;
5426 #if 0
5427   /* !!! this would prevent the removed tile from being redrawn !!! */
5428   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5429 #endif
5430 }
5431
5432 void RemoveMovingField(int x, int y)
5433 {
5434   int oldx = x, oldy = y, newx = x, newy = y;
5435   int element = Feld[x][y];
5436   int next_element = EL_UNDEFINED;
5437
5438   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5439     return;
5440
5441   if (IS_MOVING(x, y))
5442   {
5443     Moving2Blocked(x, y, &newx, &newy);
5444
5445     if (Feld[newx][newy] != EL_BLOCKED)
5446     {
5447       /* element is moving, but target field is not free (blocked), but
5448          already occupied by something different (example: acid pool);
5449          in this case, only remove the moving field, but not the target */
5450
5451       RemoveField(oldx, oldy);
5452
5453       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5454
5455       TEST_DrawLevelField(oldx, oldy);
5456
5457       return;
5458     }
5459   }
5460   else if (element == EL_BLOCKED)
5461   {
5462     Blocked2Moving(x, y, &oldx, &oldy);
5463     if (!IS_MOVING(oldx, oldy))
5464       return;
5465   }
5466
5467   if (element == EL_BLOCKED &&
5468       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5469        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5470        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5471        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5472        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5473        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5474     next_element = get_next_element(Feld[oldx][oldy]);
5475
5476   RemoveField(oldx, oldy);
5477   RemoveField(newx, newy);
5478
5479   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5480
5481   if (next_element != EL_UNDEFINED)
5482     Feld[oldx][oldy] = next_element;
5483
5484   TEST_DrawLevelField(oldx, oldy);
5485   TEST_DrawLevelField(newx, newy);
5486 }
5487
5488 void DrawDynamite(int x, int y)
5489 {
5490   int sx = SCREENX(x), sy = SCREENY(y);
5491   int graphic = el2img(Feld[x][y]);
5492   int frame;
5493
5494   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5495     return;
5496
5497   if (IS_WALKABLE_INSIDE(Back[x][y]))
5498     return;
5499
5500   if (Back[x][y])
5501     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5502   else if (Store[x][y])
5503     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5504
5505   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5506
5507   if (Back[x][y] || Store[x][y])
5508     DrawGraphicThruMask(sx, sy, graphic, frame);
5509   else
5510     DrawGraphic(sx, sy, graphic, frame);
5511 }
5512
5513 void CheckDynamite(int x, int y)
5514 {
5515   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5516   {
5517     MovDelay[x][y]--;
5518
5519     if (MovDelay[x][y] != 0)
5520     {
5521       DrawDynamite(x, y);
5522       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5523
5524       return;
5525     }
5526   }
5527
5528   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5529
5530   Bang(x, y);
5531 }
5532
5533 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5534 {
5535   boolean num_checked_players = 0;
5536   int i;
5537
5538   for (i = 0; i < MAX_PLAYERS; i++)
5539   {
5540     if (stored_player[i].active)
5541     {
5542       int sx = stored_player[i].jx;
5543       int sy = stored_player[i].jy;
5544
5545       if (num_checked_players == 0)
5546       {
5547         *sx1 = *sx2 = sx;
5548         *sy1 = *sy2 = sy;
5549       }
5550       else
5551       {
5552         *sx1 = MIN(*sx1, sx);
5553         *sy1 = MIN(*sy1, sy);
5554         *sx2 = MAX(*sx2, sx);
5555         *sy2 = MAX(*sy2, sy);
5556       }
5557
5558       num_checked_players++;
5559     }
5560   }
5561 }
5562
5563 static boolean checkIfAllPlayersFitToScreen_RND()
5564 {
5565   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5566
5567   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5568
5569   return (sx2 - sx1 < SCR_FIELDX &&
5570           sy2 - sy1 < SCR_FIELDY);
5571 }
5572
5573 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5574 {
5575   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5576
5577   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5578
5579   *sx = (sx1 + sx2) / 2;
5580   *sy = (sy1 + sy2) / 2;
5581 }
5582
5583 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5584                         boolean center_screen, boolean quick_relocation)
5585 {
5586   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5587   boolean no_delay = (tape.warp_forward);
5588   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5589   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5590
5591   if (quick_relocation)
5592   {
5593     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5594     {
5595       if (!level.shifted_relocation || center_screen)
5596       {
5597         /* quick relocation (without scrolling), with centering of screen */
5598
5599         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5600                     x > SBX_Right + MIDPOSX ? SBX_Right :
5601                     x - MIDPOSX);
5602
5603         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5604                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5605                     y - MIDPOSY);
5606       }
5607       else
5608       {
5609         /* quick relocation (without scrolling), but do not center screen */
5610
5611         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5612                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5613                                old_x - MIDPOSX);
5614
5615         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5616                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5617                                old_y - MIDPOSY);
5618
5619         int offset_x = x + (scroll_x - center_scroll_x);
5620         int offset_y = y + (scroll_y - center_scroll_y);
5621
5622         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5623                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5624                     offset_x - MIDPOSX);
5625
5626         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5627                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5628                     offset_y - MIDPOSY);
5629       }
5630     }
5631     else
5632     {
5633 #if 1
5634       if (!level.shifted_relocation || center_screen)
5635       {
5636         /* quick relocation (without scrolling), with centering of screen */
5637
5638         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5639                     x > SBX_Right + MIDPOSX ? SBX_Right :
5640                     x - MIDPOSX);
5641
5642         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5643                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5644                     y - MIDPOSY);
5645       }
5646       else
5647       {
5648         /* quick relocation (without scrolling), but do not center screen */
5649
5650         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5651                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5652                                old_x - MIDPOSX);
5653
5654         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5655                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5656                                old_y - MIDPOSY);
5657
5658         int offset_x = x + (scroll_x - center_scroll_x);
5659         int offset_y = y + (scroll_y - center_scroll_y);
5660
5661         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5662                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5663                     offset_x - MIDPOSX);
5664
5665         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5666                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5667                     offset_y - MIDPOSY);
5668       }
5669 #else
5670       /* quick relocation (without scrolling), inside visible screen area */
5671
5672       int offset = game.scroll_delay_value;
5673
5674       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5675           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5676         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5677
5678       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5679           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5680         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5681
5682       /* don't scroll over playfield boundaries */
5683       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5684         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5685
5686       /* don't scroll over playfield boundaries */
5687       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5688         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5689 #endif
5690     }
5691
5692     RedrawPlayfield(TRUE, 0,0,0,0);
5693   }
5694   else
5695   {
5696 #if 1
5697     int scroll_xx, scroll_yy;
5698
5699     if (!level.shifted_relocation || center_screen)
5700     {
5701       /* visible relocation (with scrolling), with centering of screen */
5702
5703       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5704                    x > SBX_Right + MIDPOSX ? SBX_Right :
5705                    x - MIDPOSX);
5706
5707       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5708                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5709                    y - MIDPOSY);
5710     }
5711     else
5712     {
5713       /* visible relocation (with scrolling), but do not center screen */
5714
5715       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5716                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5717                              old_x - MIDPOSX);
5718
5719       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5720                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5721                              old_y - MIDPOSY);
5722
5723       int offset_x = x + (scroll_x - center_scroll_x);
5724       int offset_y = y + (scroll_y - center_scroll_y);
5725
5726       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5727                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5728                    offset_x - MIDPOSX);
5729
5730       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5731                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5732                    offset_y - MIDPOSY);
5733     }
5734
5735 #else
5736
5737     /* visible relocation (with scrolling), with centering of screen */
5738
5739     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5740                      x > SBX_Right + MIDPOSX ? SBX_Right :
5741                      x - MIDPOSX);
5742
5743     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5744                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5745                      y - MIDPOSY);
5746 #endif
5747
5748     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5749
5750     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5751     {
5752       int dx = 0, dy = 0;
5753       int fx = FX, fy = FY;
5754
5755       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5756       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5757
5758       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5759         break;
5760
5761       scroll_x -= dx;
5762       scroll_y -= dy;
5763
5764       fx += dx * TILEX / 2;
5765       fy += dy * TILEY / 2;
5766
5767       ScrollLevel(dx, dy);
5768       DrawAllPlayers();
5769
5770       /* scroll in two steps of half tile size to make things smoother */
5771       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5772       FlushDisplay();
5773       Delay(wait_delay_value);
5774
5775       /* scroll second step to align at full tile size */
5776       BackToFront();
5777       Delay(wait_delay_value);
5778     }
5779
5780     DrawAllPlayers();
5781     BackToFront();
5782     Delay(wait_delay_value);
5783   }
5784 }
5785
5786 void RelocatePlayer(int jx, int jy, int el_player_raw)
5787 {
5788   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5789   int player_nr = GET_PLAYER_NR(el_player);
5790   struct PlayerInfo *player = &stored_player[player_nr];
5791   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5792   boolean no_delay = (tape.warp_forward);
5793   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5794   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5795   int old_jx = player->jx;
5796   int old_jy = player->jy;
5797   int old_element = Feld[old_jx][old_jy];
5798   int element = Feld[jx][jy];
5799   boolean player_relocated = (old_jx != jx || old_jy != jy);
5800
5801   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5802   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5803   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5804   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5805   int leave_side_horiz = move_dir_horiz;
5806   int leave_side_vert  = move_dir_vert;
5807   int enter_side = enter_side_horiz | enter_side_vert;
5808   int leave_side = leave_side_horiz | leave_side_vert;
5809
5810   if (player->GameOver)         /* do not reanimate dead player */
5811     return;
5812
5813   if (!player_relocated)        /* no need to relocate the player */
5814     return;
5815
5816   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5817   {
5818     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5819     DrawLevelField(jx, jy);
5820   }
5821
5822   if (player->present)
5823   {
5824     while (player->MovPos)
5825     {
5826       ScrollPlayer(player, SCROLL_GO_ON);
5827       ScrollScreen(NULL, SCROLL_GO_ON);
5828
5829       AdvanceFrameAndPlayerCounters(player->index_nr);
5830
5831       DrawPlayer(player);
5832
5833       BackToFront();
5834       Delay(wait_delay_value);
5835     }
5836
5837     DrawPlayer(player);         /* needed here only to cleanup last field */
5838     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5839
5840     player->is_moving = FALSE;
5841   }
5842
5843   if (IS_CUSTOM_ELEMENT(old_element))
5844     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5845                                CE_LEFT_BY_PLAYER,
5846                                player->index_bit, leave_side);
5847
5848   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5849                                       CE_PLAYER_LEAVES_X,
5850                                       player->index_bit, leave_side);
5851
5852   Feld[jx][jy] = el_player;
5853   InitPlayerField(jx, jy, el_player, TRUE);
5854
5855   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5856      possible that the relocation target field did not contain a player element,
5857      but a walkable element, to which the new player was relocated -- in this
5858      case, restore that (already initialized!) element on the player field */
5859   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5860   {
5861     Feld[jx][jy] = element;     /* restore previously existing element */
5862 #if 0
5863     /* !!! do not initialize already initialized element a second time !!! */
5864     /* (this causes at least problems with "element creation" CE trigger for
5865        already existing elements, and existing Sokoban fields counted twice) */
5866     InitField(jx, jy, FALSE);
5867 #endif
5868   }
5869
5870   /* only visually relocate centered player */
5871   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5872                      FALSE, level.instant_relocation);
5873
5874   TestIfPlayerTouchesBadThing(jx, jy);
5875   TestIfPlayerTouchesCustomElement(jx, jy);
5876
5877   if (IS_CUSTOM_ELEMENT(element))
5878     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5879                                player->index_bit, enter_side);
5880
5881   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5882                                       player->index_bit, enter_side);
5883
5884 #if 1
5885   if (player->is_switching)
5886   {
5887     /* ensure that relocation while still switching an element does not cause
5888        a new element to be treated as also switched directly after relocation
5889        (this is important for teleporter switches that teleport the player to
5890        a place where another teleporter switch is in the same direction, which
5891        would then incorrectly be treated as immediately switched before the
5892        direction key that caused the switch was released) */
5893
5894     player->switch_x += jx - old_jx;
5895     player->switch_y += jy - old_jy;
5896   }
5897 #endif
5898 }
5899
5900 void Explode(int ex, int ey, int phase, int mode)
5901 {
5902   int x, y;
5903   int last_phase;
5904   int border_element;
5905
5906   /* !!! eliminate this variable !!! */
5907   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5908
5909   if (game.explosions_delayed)
5910   {
5911     ExplodeField[ex][ey] = mode;
5912     return;
5913   }
5914
5915   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5916   {
5917     int center_element = Feld[ex][ey];
5918     int artwork_element, explosion_element;     /* set these values later */
5919
5920 #if 0
5921     /* --- This is only really needed (and now handled) in "Impact()". --- */
5922     /* do not explode moving elements that left the explode field in time */
5923     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5924         center_element == EL_EMPTY &&
5925         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5926       return;
5927 #endif
5928
5929 #if 0
5930     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5931     if (mode == EX_TYPE_NORMAL ||
5932         mode == EX_TYPE_CENTER ||
5933         mode == EX_TYPE_CROSS)
5934       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5935 #endif
5936
5937     /* remove things displayed in background while burning dynamite */
5938     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5939       Back[ex][ey] = 0;
5940
5941     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5942     {
5943       /* put moving element to center field (and let it explode there) */
5944       center_element = MovingOrBlocked2Element(ex, ey);
5945       RemoveMovingField(ex, ey);
5946       Feld[ex][ey] = center_element;
5947     }
5948
5949     /* now "center_element" is finally determined -- set related values now */
5950     artwork_element = center_element;           /* for custom player artwork */
5951     explosion_element = center_element;         /* for custom player artwork */
5952
5953     if (IS_PLAYER(ex, ey))
5954     {
5955       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5956
5957       artwork_element = stored_player[player_nr].artwork_element;
5958
5959       if (level.use_explosion_element[player_nr])
5960       {
5961         explosion_element = level.explosion_element[player_nr];
5962         artwork_element = explosion_element;
5963       }
5964     }
5965
5966 #if 1
5967     if (mode == EX_TYPE_NORMAL ||
5968         mode == EX_TYPE_CENTER ||
5969         mode == EX_TYPE_CROSS)
5970       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5971 #endif
5972
5973     last_phase = element_info[explosion_element].explosion_delay + 1;
5974
5975     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5976     {
5977       int xx = x - ex + 1;
5978       int yy = y - ey + 1;
5979       int element;
5980
5981       if (!IN_LEV_FIELD(x, y) ||
5982           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5983           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5984         continue;
5985
5986       element = Feld[x][y];
5987
5988       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5989       {
5990         element = MovingOrBlocked2Element(x, y);
5991
5992         if (!IS_EXPLOSION_PROOF(element))
5993           RemoveMovingField(x, y);
5994       }
5995
5996       /* indestructible elements can only explode in center (but not flames) */
5997       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5998                                            mode == EX_TYPE_BORDER)) ||
5999           element == EL_FLAMES)
6000         continue;
6001
6002       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6003          behaviour, for example when touching a yamyam that explodes to rocks
6004          with active deadly shield, a rock is created under the player !!! */
6005       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6006 #if 0
6007       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6008           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6009            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6010 #else
6011       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6012 #endif
6013       {
6014         if (IS_ACTIVE_BOMB(element))
6015         {
6016           /* re-activate things under the bomb like gate or penguin */
6017           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6018           Back[x][y] = 0;
6019         }
6020
6021         continue;
6022       }
6023
6024       /* save walkable background elements while explosion on same tile */
6025       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6026           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6027         Back[x][y] = element;
6028
6029       /* ignite explodable elements reached by other explosion */
6030       if (element == EL_EXPLOSION)
6031         element = Store2[x][y];
6032
6033       if (AmoebaNr[x][y] &&
6034           (element == EL_AMOEBA_FULL ||
6035            element == EL_BD_AMOEBA ||
6036            element == EL_AMOEBA_GROWING))
6037       {
6038         AmoebaCnt[AmoebaNr[x][y]]--;
6039         AmoebaCnt2[AmoebaNr[x][y]]--;
6040       }
6041
6042       RemoveField(x, y);
6043
6044       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6045       {
6046         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6047
6048         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6049
6050         if (PLAYERINFO(ex, ey)->use_murphy)
6051           Store[x][y] = EL_EMPTY;
6052       }
6053
6054       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6055          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6056       else if (ELEM_IS_PLAYER(center_element))
6057         Store[x][y] = EL_EMPTY;
6058       else if (center_element == EL_YAMYAM)
6059         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6060       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6061         Store[x][y] = element_info[center_element].content.e[xx][yy];
6062 #if 1
6063       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6064          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6065          otherwise) -- FIX THIS !!! */
6066       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6067         Store[x][y] = element_info[element].content.e[1][1];
6068 #else
6069       else if (!CAN_EXPLODE(element))
6070         Store[x][y] = element_info[element].content.e[1][1];
6071 #endif
6072       else
6073         Store[x][y] = EL_EMPTY;
6074
6075       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6076           center_element == EL_AMOEBA_TO_DIAMOND)
6077         Store2[x][y] = element;
6078
6079       Feld[x][y] = EL_EXPLOSION;
6080       GfxElement[x][y] = artwork_element;
6081
6082       ExplodePhase[x][y] = 1;
6083       ExplodeDelay[x][y] = last_phase;
6084
6085       Stop[x][y] = TRUE;
6086     }
6087
6088     if (center_element == EL_YAMYAM)
6089       game.yamyam_content_nr =
6090         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6091
6092     return;
6093   }
6094
6095   if (Stop[ex][ey])
6096     return;
6097
6098   x = ex;
6099   y = ey;
6100
6101   if (phase == 1)
6102     GfxFrame[x][y] = 0;         /* restart explosion animation */
6103
6104   last_phase = ExplodeDelay[x][y];
6105
6106   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6107
6108 #ifdef DEBUG
6109
6110   /* activate this even in non-DEBUG version until cause for crash in
6111      getGraphicAnimationFrame() (see below) is found and eliminated */
6112
6113 #endif
6114 #if 1
6115
6116 #if 1
6117   /* this can happen if the player leaves an explosion just in time */
6118   if (GfxElement[x][y] == EL_UNDEFINED)
6119     GfxElement[x][y] = EL_EMPTY;
6120 #else
6121   if (GfxElement[x][y] == EL_UNDEFINED)
6122   {
6123     printf("\n\n");
6124     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6125     printf("Explode(): This should never happen!\n");
6126     printf("\n\n");
6127
6128     GfxElement[x][y] = EL_EMPTY;
6129   }
6130 #endif
6131
6132 #endif
6133
6134   border_element = Store2[x][y];
6135   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6136     border_element = StorePlayer[x][y];
6137
6138   if (phase == element_info[border_element].ignition_delay ||
6139       phase == last_phase)
6140   {
6141     boolean border_explosion = FALSE;
6142
6143     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6144         !PLAYER_EXPLOSION_PROTECTED(x, y))
6145     {
6146       KillPlayerUnlessExplosionProtected(x, y);
6147       border_explosion = TRUE;
6148     }
6149     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6150     {
6151       Feld[x][y] = Store2[x][y];
6152       Store2[x][y] = 0;
6153       Bang(x, y);
6154       border_explosion = TRUE;
6155     }
6156     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6157     {
6158       AmoebeUmwandeln(x, y);
6159       Store2[x][y] = 0;
6160       border_explosion = TRUE;
6161     }
6162
6163     /* if an element just explodes due to another explosion (chain-reaction),
6164        do not immediately end the new explosion when it was the last frame of
6165        the explosion (as it would be done in the following "if"-statement!) */
6166     if (border_explosion && phase == last_phase)
6167       return;
6168   }
6169
6170   if (phase == last_phase)
6171   {
6172     int element;
6173
6174     element = Feld[x][y] = Store[x][y];
6175     Store[x][y] = Store2[x][y] = 0;
6176     GfxElement[x][y] = EL_UNDEFINED;
6177
6178     /* player can escape from explosions and might therefore be still alive */
6179     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6180         element <= EL_PLAYER_IS_EXPLODING_4)
6181     {
6182       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6183       int explosion_element = EL_PLAYER_1 + player_nr;
6184       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6185       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6186
6187       if (level.use_explosion_element[player_nr])
6188         explosion_element = level.explosion_element[player_nr];
6189
6190       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6191                     element_info[explosion_element].content.e[xx][yy]);
6192     }
6193
6194     /* restore probably existing indestructible background element */
6195     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6196       element = Feld[x][y] = Back[x][y];
6197     Back[x][y] = 0;
6198
6199     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6200     GfxDir[x][y] = MV_NONE;
6201     ChangeDelay[x][y] = 0;
6202     ChangePage[x][y] = -1;
6203
6204 #if USE_NEW_CUSTOM_VALUE
6205     CustomValue[x][y] = 0;
6206 #endif
6207
6208     InitField_WithBug2(x, y, FALSE);
6209
6210     TEST_DrawLevelField(x, y);
6211
6212     TestIfElementTouchesCustomElement(x, y);
6213
6214     if (GFX_CRUMBLED(element))
6215       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6216
6217     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6218       StorePlayer[x][y] = 0;
6219
6220     if (ELEM_IS_PLAYER(element))
6221       RelocatePlayer(x, y, element);
6222   }
6223   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6224   {
6225     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6226     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6227
6228     if (phase == delay)
6229       TEST_DrawLevelFieldCrumbled(x, y);
6230
6231     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6232     {
6233       DrawLevelElement(x, y, Back[x][y]);
6234       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6235     }
6236     else if (IS_WALKABLE_UNDER(Back[x][y]))
6237     {
6238       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6239       DrawLevelElementThruMask(x, y, Back[x][y]);
6240     }
6241     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6242       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6243   }
6244 }
6245
6246 void DynaExplode(int ex, int ey)
6247 {
6248   int i, j;
6249   int dynabomb_element = Feld[ex][ey];
6250   int dynabomb_size = 1;
6251   boolean dynabomb_xl = FALSE;
6252   struct PlayerInfo *player;
6253   static int xy[4][2] =
6254   {
6255     { 0, -1 },
6256     { -1, 0 },
6257     { +1, 0 },
6258     { 0, +1 }
6259   };
6260
6261   if (IS_ACTIVE_BOMB(dynabomb_element))
6262   {
6263     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6264     dynabomb_size = player->dynabomb_size;
6265     dynabomb_xl = player->dynabomb_xl;
6266     player->dynabombs_left++;
6267   }
6268
6269   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6270
6271   for (i = 0; i < NUM_DIRECTIONS; i++)
6272   {
6273     for (j = 1; j <= dynabomb_size; j++)
6274     {
6275       int x = ex + j * xy[i][0];
6276       int y = ey + j * xy[i][1];
6277       int element;
6278
6279       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6280         break;
6281
6282       element = Feld[x][y];
6283
6284       /* do not restart explosions of fields with active bombs */
6285       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6286         continue;
6287
6288       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6289
6290       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6291           !IS_DIGGABLE(element) && !dynabomb_xl)
6292         break;
6293     }
6294   }
6295 }
6296
6297 void Bang(int x, int y)
6298 {
6299   int element = MovingOrBlocked2Element(x, y);
6300   int explosion_type = EX_TYPE_NORMAL;
6301
6302   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6303   {
6304     struct PlayerInfo *player = PLAYERINFO(x, y);
6305
6306 #if USE_FIX_CE_ACTION_WITH_PLAYER
6307     element = Feld[x][y] = player->initial_element;
6308 #else
6309     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6310                             player->element_nr);
6311 #endif
6312
6313     if (level.use_explosion_element[player->index_nr])
6314     {
6315       int explosion_element = level.explosion_element[player->index_nr];
6316
6317       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6318         explosion_type = EX_TYPE_CROSS;
6319       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6320         explosion_type = EX_TYPE_CENTER;
6321     }
6322   }
6323
6324   switch (element)
6325   {
6326     case EL_BUG:
6327     case EL_SPACESHIP:
6328     case EL_BD_BUTTERFLY:
6329     case EL_BD_FIREFLY:
6330     case EL_YAMYAM:
6331     case EL_DARK_YAMYAM:
6332     case EL_ROBOT:
6333     case EL_PACMAN:
6334     case EL_MOLE:
6335       RaiseScoreElement(element);
6336       break;
6337
6338     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6339     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6340     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6341     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6342     case EL_DYNABOMB_INCREASE_NUMBER:
6343     case EL_DYNABOMB_INCREASE_SIZE:
6344     case EL_DYNABOMB_INCREASE_POWER:
6345       explosion_type = EX_TYPE_DYNA;
6346       break;
6347
6348     case EL_DC_LANDMINE:
6349 #if 0
6350     case EL_EM_EXIT_OPEN:
6351     case EL_EM_STEEL_EXIT_OPEN:
6352 #endif
6353       explosion_type = EX_TYPE_CENTER;
6354       break;
6355
6356     case EL_PENGUIN:
6357     case EL_LAMP:
6358     case EL_LAMP_ACTIVE:
6359     case EL_AMOEBA_TO_DIAMOND:
6360       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6361         explosion_type = EX_TYPE_CENTER;
6362       break;
6363
6364     default:
6365       if (element_info[element].explosion_type == EXPLODES_CROSS)
6366         explosion_type = EX_TYPE_CROSS;
6367       else if (element_info[element].explosion_type == EXPLODES_1X1)
6368         explosion_type = EX_TYPE_CENTER;
6369       break;
6370   }
6371
6372   if (explosion_type == EX_TYPE_DYNA)
6373     DynaExplode(x, y);
6374   else
6375     Explode(x, y, EX_PHASE_START, explosion_type);
6376
6377   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6378 }
6379
6380 void SplashAcid(int x, int y)
6381 {
6382   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6383       (!IN_LEV_FIELD(x - 1, y - 2) ||
6384        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6385     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6386
6387   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6388       (!IN_LEV_FIELD(x + 1, y - 2) ||
6389        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6390     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6391
6392   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6393 }
6394
6395 static void InitBeltMovement()
6396 {
6397   static int belt_base_element[4] =
6398   {
6399     EL_CONVEYOR_BELT_1_LEFT,
6400     EL_CONVEYOR_BELT_2_LEFT,
6401     EL_CONVEYOR_BELT_3_LEFT,
6402     EL_CONVEYOR_BELT_4_LEFT
6403   };
6404   static int belt_base_active_element[4] =
6405   {
6406     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6407     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6408     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6409     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6410   };
6411
6412   int x, y, i, j;
6413
6414   /* set frame order for belt animation graphic according to belt direction */
6415   for (i = 0; i < NUM_BELTS; i++)
6416   {
6417     int belt_nr = i;
6418
6419     for (j = 0; j < NUM_BELT_PARTS; j++)
6420     {
6421       int element = belt_base_active_element[belt_nr] + j;
6422       int graphic_1 = el2img(element);
6423       int graphic_2 = el2panelimg(element);
6424
6425       if (game.belt_dir[i] == MV_LEFT)
6426       {
6427         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6428         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6429       }
6430       else
6431       {
6432         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6433         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6434       }
6435     }
6436   }
6437
6438   SCAN_PLAYFIELD(x, y)
6439   {
6440     int element = Feld[x][y];
6441
6442     for (i = 0; i < NUM_BELTS; i++)
6443     {
6444       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6445       {
6446         int e_belt_nr = getBeltNrFromBeltElement(element);
6447         int belt_nr = i;
6448
6449         if (e_belt_nr == belt_nr)
6450         {
6451           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6452
6453           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6454         }
6455       }
6456     }
6457   }
6458 }
6459
6460 static void ToggleBeltSwitch(int x, int y)
6461 {
6462   static int belt_base_element[4] =
6463   {
6464     EL_CONVEYOR_BELT_1_LEFT,
6465     EL_CONVEYOR_BELT_2_LEFT,
6466     EL_CONVEYOR_BELT_3_LEFT,
6467     EL_CONVEYOR_BELT_4_LEFT
6468   };
6469   static int belt_base_active_element[4] =
6470   {
6471     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6472     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6473     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6474     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6475   };
6476   static int belt_base_switch_element[4] =
6477   {
6478     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6479     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6480     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6481     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6482   };
6483   static int belt_move_dir[4] =
6484   {
6485     MV_LEFT,
6486     MV_NONE,
6487     MV_RIGHT,
6488     MV_NONE,
6489   };
6490
6491   int element = Feld[x][y];
6492   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6493   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6494   int belt_dir = belt_move_dir[belt_dir_nr];
6495   int xx, yy, i;
6496
6497   if (!IS_BELT_SWITCH(element))
6498     return;
6499
6500   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6501   game.belt_dir[belt_nr] = belt_dir;
6502
6503   if (belt_dir_nr == 3)
6504     belt_dir_nr = 1;
6505
6506   /* set frame order for belt animation graphic according to belt direction */
6507   for (i = 0; i < NUM_BELT_PARTS; i++)
6508   {
6509     int element = belt_base_active_element[belt_nr] + i;
6510     int graphic_1 = el2img(element);
6511     int graphic_2 = el2panelimg(element);
6512
6513     if (belt_dir == MV_LEFT)
6514     {
6515       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6516       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6517     }
6518     else
6519     {
6520       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6521       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6522     }
6523   }
6524
6525   SCAN_PLAYFIELD(xx, yy)
6526   {
6527     int element = Feld[xx][yy];
6528
6529     if (IS_BELT_SWITCH(element))
6530     {
6531       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6532
6533       if (e_belt_nr == belt_nr)
6534       {
6535         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6536         TEST_DrawLevelField(xx, yy);
6537       }
6538     }
6539     else if (IS_BELT(element) && belt_dir != MV_NONE)
6540     {
6541       int e_belt_nr = getBeltNrFromBeltElement(element);
6542
6543       if (e_belt_nr == belt_nr)
6544       {
6545         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6546
6547         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6548         TEST_DrawLevelField(xx, yy);
6549       }
6550     }
6551     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6552     {
6553       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6554
6555       if (e_belt_nr == belt_nr)
6556       {
6557         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6558
6559         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6560         TEST_DrawLevelField(xx, yy);
6561       }
6562     }
6563   }
6564 }
6565
6566 static void ToggleSwitchgateSwitch(int x, int y)
6567 {
6568   int xx, yy;
6569
6570   game.switchgate_pos = !game.switchgate_pos;
6571
6572   SCAN_PLAYFIELD(xx, yy)
6573   {
6574     int element = Feld[xx][yy];
6575
6576 #if !USE_BOTH_SWITCHGATE_SWITCHES
6577     if (element == EL_SWITCHGATE_SWITCH_UP ||
6578         element == EL_SWITCHGATE_SWITCH_DOWN)
6579     {
6580       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6581       TEST_DrawLevelField(xx, yy);
6582     }
6583     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6584              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6585     {
6586       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6587       TEST_DrawLevelField(xx, yy);
6588     }
6589 #else
6590     if (element == EL_SWITCHGATE_SWITCH_UP)
6591     {
6592       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6593       TEST_DrawLevelField(xx, yy);
6594     }
6595     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6596     {
6597       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6598       TEST_DrawLevelField(xx, yy);
6599     }
6600     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6601     {
6602       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6603       TEST_DrawLevelField(xx, yy);
6604     }
6605     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6606     {
6607       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6608       TEST_DrawLevelField(xx, yy);
6609     }
6610 #endif
6611     else if (element == EL_SWITCHGATE_OPEN ||
6612              element == EL_SWITCHGATE_OPENING)
6613     {
6614       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6615
6616       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6617     }
6618     else if (element == EL_SWITCHGATE_CLOSED ||
6619              element == EL_SWITCHGATE_CLOSING)
6620     {
6621       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6622
6623       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6624     }
6625   }
6626 }
6627
6628 static int getInvisibleActiveFromInvisibleElement(int element)
6629 {
6630   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6631           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6632           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6633           element);
6634 }
6635
6636 static int getInvisibleFromInvisibleActiveElement(int element)
6637 {
6638   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6639           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6640           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6641           element);
6642 }
6643
6644 static void RedrawAllLightSwitchesAndInvisibleElements()
6645 {
6646   int x, y;
6647
6648   SCAN_PLAYFIELD(x, y)
6649   {
6650     int element = Feld[x][y];
6651
6652     if (element == EL_LIGHT_SWITCH &&
6653         game.light_time_left > 0)
6654     {
6655       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6656       TEST_DrawLevelField(x, y);
6657     }
6658     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6659              game.light_time_left == 0)
6660     {
6661       Feld[x][y] = EL_LIGHT_SWITCH;
6662       TEST_DrawLevelField(x, y);
6663     }
6664     else if (element == EL_EMC_DRIPPER &&
6665              game.light_time_left > 0)
6666     {
6667       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6668       TEST_DrawLevelField(x, y);
6669     }
6670     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6671              game.light_time_left == 0)
6672     {
6673       Feld[x][y] = EL_EMC_DRIPPER;
6674       TEST_DrawLevelField(x, y);
6675     }
6676     else if (element == EL_INVISIBLE_STEELWALL ||
6677              element == EL_INVISIBLE_WALL ||
6678              element == EL_INVISIBLE_SAND)
6679     {
6680       if (game.light_time_left > 0)
6681         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6682
6683       TEST_DrawLevelField(x, y);
6684
6685       /* uncrumble neighbour fields, if needed */
6686       if (element == EL_INVISIBLE_SAND)
6687         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6688     }
6689     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6690              element == EL_INVISIBLE_WALL_ACTIVE ||
6691              element == EL_INVISIBLE_SAND_ACTIVE)
6692     {
6693       if (game.light_time_left == 0)
6694         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6695
6696       TEST_DrawLevelField(x, y);
6697
6698       /* re-crumble neighbour fields, if needed */
6699       if (element == EL_INVISIBLE_SAND)
6700         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6701     }
6702   }
6703 }
6704
6705 static void RedrawAllInvisibleElementsForLenses()
6706 {
6707   int x, y;
6708
6709   SCAN_PLAYFIELD(x, y)
6710   {
6711     int element = Feld[x][y];
6712
6713     if (element == EL_EMC_DRIPPER &&
6714         game.lenses_time_left > 0)
6715     {
6716       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6717       TEST_DrawLevelField(x, y);
6718     }
6719     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6720              game.lenses_time_left == 0)
6721     {
6722       Feld[x][y] = EL_EMC_DRIPPER;
6723       TEST_DrawLevelField(x, y);
6724     }
6725     else if (element == EL_INVISIBLE_STEELWALL ||
6726              element == EL_INVISIBLE_WALL ||
6727              element == EL_INVISIBLE_SAND)
6728     {
6729       if (game.lenses_time_left > 0)
6730         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6731
6732       TEST_DrawLevelField(x, y);
6733
6734       /* uncrumble neighbour fields, if needed */
6735       if (element == EL_INVISIBLE_SAND)
6736         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6737     }
6738     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6739              element == EL_INVISIBLE_WALL_ACTIVE ||
6740              element == EL_INVISIBLE_SAND_ACTIVE)
6741     {
6742       if (game.lenses_time_left == 0)
6743         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6744
6745       TEST_DrawLevelField(x, y);
6746
6747       /* re-crumble neighbour fields, if needed */
6748       if (element == EL_INVISIBLE_SAND)
6749         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6750     }
6751   }
6752 }
6753
6754 static void RedrawAllInvisibleElementsForMagnifier()
6755 {
6756   int x, y;
6757
6758   SCAN_PLAYFIELD(x, y)
6759   {
6760     int element = Feld[x][y];
6761
6762     if (element == EL_EMC_FAKE_GRASS &&
6763         game.magnify_time_left > 0)
6764     {
6765       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6766       TEST_DrawLevelField(x, y);
6767     }
6768     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6769              game.magnify_time_left == 0)
6770     {
6771       Feld[x][y] = EL_EMC_FAKE_GRASS;
6772       TEST_DrawLevelField(x, y);
6773     }
6774     else if (IS_GATE_GRAY(element) &&
6775              game.magnify_time_left > 0)
6776     {
6777       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6778                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6779                     IS_EM_GATE_GRAY(element) ?
6780                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6781                     IS_EMC_GATE_GRAY(element) ?
6782                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6783                     IS_DC_GATE_GRAY(element) ?
6784                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6785                     element);
6786       TEST_DrawLevelField(x, y);
6787     }
6788     else if (IS_GATE_GRAY_ACTIVE(element) &&
6789              game.magnify_time_left == 0)
6790     {
6791       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6792                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6793                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6794                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6795                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6796                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6797                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6798                     EL_DC_GATE_WHITE_GRAY :
6799                     element);
6800       TEST_DrawLevelField(x, y);
6801     }
6802   }
6803 }
6804
6805 static void ToggleLightSwitch(int x, int y)
6806 {
6807   int element = Feld[x][y];
6808
6809   game.light_time_left =
6810     (element == EL_LIGHT_SWITCH ?
6811      level.time_light * FRAMES_PER_SECOND : 0);
6812
6813   RedrawAllLightSwitchesAndInvisibleElements();
6814 }
6815
6816 static void ActivateTimegateSwitch(int x, int y)
6817 {
6818   int xx, yy;
6819
6820   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6821
6822   SCAN_PLAYFIELD(xx, yy)
6823   {
6824     int element = Feld[xx][yy];
6825
6826     if (element == EL_TIMEGATE_CLOSED ||
6827         element == EL_TIMEGATE_CLOSING)
6828     {
6829       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6830       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6831     }
6832
6833     /*
6834     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6835     {
6836       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6837       TEST_DrawLevelField(xx, yy);
6838     }
6839     */
6840
6841   }
6842
6843 #if 1
6844   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6845                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6846 #else
6847   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6848 #endif
6849 }
6850
6851 void Impact(int x, int y)
6852 {
6853   boolean last_line = (y == lev_fieldy - 1);
6854   boolean object_hit = FALSE;
6855   boolean impact = (last_line || object_hit);
6856   int element = Feld[x][y];
6857   int smashed = EL_STEELWALL;
6858
6859   if (!last_line)       /* check if element below was hit */
6860   {
6861     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6862       return;
6863
6864     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6865                                          MovDir[x][y + 1] != MV_DOWN ||
6866                                          MovPos[x][y + 1] <= TILEY / 2));
6867
6868     /* do not smash moving elements that left the smashed field in time */
6869     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6870         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6871       object_hit = FALSE;
6872
6873 #if USE_QUICKSAND_IMPACT_BUGFIX
6874     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6875     {
6876       RemoveMovingField(x, y + 1);
6877       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6878       Feld[x][y + 2] = EL_ROCK;
6879       TEST_DrawLevelField(x, y + 2);
6880
6881       object_hit = TRUE;
6882     }
6883
6884     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6885     {
6886       RemoveMovingField(x, y + 1);
6887       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6888       Feld[x][y + 2] = EL_ROCK;
6889       TEST_DrawLevelField(x, y + 2);
6890
6891       object_hit = TRUE;
6892     }
6893 #endif
6894
6895     if (object_hit)
6896       smashed = MovingOrBlocked2Element(x, y + 1);
6897
6898     impact = (last_line || object_hit);
6899   }
6900
6901   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6902   {
6903     SplashAcid(x, y + 1);
6904     return;
6905   }
6906
6907   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6908   /* only reset graphic animation if graphic really changes after impact */
6909   if (impact &&
6910       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6911   {
6912     ResetGfxAnimation(x, y);
6913     TEST_DrawLevelField(x, y);
6914   }
6915
6916   if (impact && CAN_EXPLODE_IMPACT(element))
6917   {
6918     Bang(x, y);
6919     return;
6920   }
6921   else if (impact && element == EL_PEARL &&
6922            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6923   {
6924     ResetGfxAnimation(x, y);
6925
6926     Feld[x][y] = EL_PEARL_BREAKING;
6927     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6928     return;
6929   }
6930   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6931   {
6932     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6933
6934     return;
6935   }
6936
6937   if (impact && element == EL_AMOEBA_DROP)
6938   {
6939     if (object_hit && IS_PLAYER(x, y + 1))
6940       KillPlayerUnlessEnemyProtected(x, y + 1);
6941     else if (object_hit && smashed == EL_PENGUIN)
6942       Bang(x, y + 1);
6943     else
6944     {
6945       Feld[x][y] = EL_AMOEBA_GROWING;
6946       Store[x][y] = EL_AMOEBA_WET;
6947
6948       ResetRandomAnimationValue(x, y);
6949     }
6950     return;
6951   }
6952
6953   if (object_hit)               /* check which object was hit */
6954   {
6955     if ((CAN_PASS_MAGIC_WALL(element) && 
6956          (smashed == EL_MAGIC_WALL ||
6957           smashed == EL_BD_MAGIC_WALL)) ||
6958         (CAN_PASS_DC_MAGIC_WALL(element) &&
6959          smashed == EL_DC_MAGIC_WALL))
6960     {
6961       int xx, yy;
6962       int activated_magic_wall =
6963         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6964          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6965          EL_DC_MAGIC_WALL_ACTIVE);
6966
6967       /* activate magic wall / mill */
6968       SCAN_PLAYFIELD(xx, yy)
6969       {
6970         if (Feld[xx][yy] == smashed)
6971           Feld[xx][yy] = activated_magic_wall;
6972       }
6973
6974       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6975       game.magic_wall_active = TRUE;
6976
6977       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6978                             SND_MAGIC_WALL_ACTIVATING :
6979                             smashed == EL_BD_MAGIC_WALL ?
6980                             SND_BD_MAGIC_WALL_ACTIVATING :
6981                             SND_DC_MAGIC_WALL_ACTIVATING));
6982     }
6983
6984     if (IS_PLAYER(x, y + 1))
6985     {
6986       if (CAN_SMASH_PLAYER(element))
6987       {
6988         KillPlayerUnlessEnemyProtected(x, y + 1);
6989         return;
6990       }
6991     }
6992     else if (smashed == EL_PENGUIN)
6993     {
6994       if (CAN_SMASH_PLAYER(element))
6995       {
6996         Bang(x, y + 1);
6997         return;
6998       }
6999     }
7000     else if (element == EL_BD_DIAMOND)
7001     {
7002       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7003       {
7004         Bang(x, y + 1);
7005         return;
7006       }
7007     }
7008     else if (((element == EL_SP_INFOTRON ||
7009                element == EL_SP_ZONK) &&
7010               (smashed == EL_SP_SNIKSNAK ||
7011                smashed == EL_SP_ELECTRON ||
7012                smashed == EL_SP_DISK_ORANGE)) ||
7013              (element == EL_SP_INFOTRON &&
7014               smashed == EL_SP_DISK_YELLOW))
7015     {
7016       Bang(x, y + 1);
7017       return;
7018     }
7019     else if (CAN_SMASH_EVERYTHING(element))
7020     {
7021       if (IS_CLASSIC_ENEMY(smashed) ||
7022           CAN_EXPLODE_SMASHED(smashed))
7023       {
7024         Bang(x, y + 1);
7025         return;
7026       }
7027       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7028       {
7029         if (smashed == EL_LAMP ||
7030             smashed == EL_LAMP_ACTIVE)
7031         {
7032           Bang(x, y + 1);
7033           return;
7034         }
7035         else if (smashed == EL_NUT)
7036         {
7037           Feld[x][y + 1] = EL_NUT_BREAKING;
7038           PlayLevelSound(x, y, SND_NUT_BREAKING);
7039           RaiseScoreElement(EL_NUT);
7040           return;
7041         }
7042         else if (smashed == EL_PEARL)
7043         {
7044           ResetGfxAnimation(x, y);
7045
7046           Feld[x][y + 1] = EL_PEARL_BREAKING;
7047           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7048           return;
7049         }
7050         else if (smashed == EL_DIAMOND)
7051         {
7052           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7053           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7054           return;
7055         }
7056         else if (IS_BELT_SWITCH(smashed))
7057         {
7058           ToggleBeltSwitch(x, y + 1);
7059         }
7060         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7061                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7062                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7063                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7064         {
7065           ToggleSwitchgateSwitch(x, y + 1);
7066         }
7067         else if (smashed == EL_LIGHT_SWITCH ||
7068                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7069         {
7070           ToggleLightSwitch(x, y + 1);
7071         }
7072         else
7073         {
7074 #if 0
7075           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7076 #endif
7077
7078           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7079
7080           CheckElementChangeBySide(x, y + 1, smashed, element,
7081                                    CE_SWITCHED, CH_SIDE_TOP);
7082           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7083                                             CH_SIDE_TOP);
7084         }
7085       }
7086       else
7087       {
7088         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7089       }
7090     }
7091   }
7092
7093   /* play sound of magic wall / mill */
7094   if (!last_line &&
7095       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7096        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7097        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7098   {
7099     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7100       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7101     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7102       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7103     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7104       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7105
7106     return;
7107   }
7108
7109   /* play sound of object that hits the ground */
7110   if (last_line || object_hit)
7111     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7112 }
7113
7114 inline static void TurnRoundExt(int x, int y)
7115 {
7116   static struct
7117   {
7118     int dx, dy;
7119   } move_xy[] =
7120   {
7121     {  0,  0 },
7122     { -1,  0 },
7123     { +1,  0 },
7124     {  0,  0 },
7125     {  0, -1 },
7126     {  0,  0 }, { 0, 0 }, { 0, 0 },
7127     {  0, +1 }
7128   };
7129   static struct
7130   {
7131     int left, right, back;
7132   } turn[] =
7133   {
7134     { 0,        0,              0        },
7135     { MV_DOWN,  MV_UP,          MV_RIGHT },
7136     { MV_UP,    MV_DOWN,        MV_LEFT  },
7137     { 0,        0,              0        },
7138     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7139     { 0,        0,              0        },
7140     { 0,        0,              0        },
7141     { 0,        0,              0        },
7142     { MV_RIGHT, MV_LEFT,        MV_UP    }
7143   };
7144
7145   int element = Feld[x][y];
7146   int move_pattern = element_info[element].move_pattern;
7147
7148   int old_move_dir = MovDir[x][y];
7149   int left_dir  = turn[old_move_dir].left;
7150   int right_dir = turn[old_move_dir].right;
7151   int back_dir  = turn[old_move_dir].back;
7152
7153   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7154   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7155   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7156   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7157
7158   int left_x  = x + left_dx,  left_y  = y + left_dy;
7159   int right_x = x + right_dx, right_y = y + right_dy;
7160   int move_x  = x + move_dx,  move_y  = y + move_dy;
7161
7162   int xx, yy;
7163
7164   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7165   {
7166     TestIfBadThingTouchesOtherBadThing(x, y);
7167
7168     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7169       MovDir[x][y] = right_dir;
7170     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7171       MovDir[x][y] = left_dir;
7172
7173     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7174       MovDelay[x][y] = 9;
7175     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7176       MovDelay[x][y] = 1;
7177   }
7178   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7179   {
7180     TestIfBadThingTouchesOtherBadThing(x, y);
7181
7182     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7183       MovDir[x][y] = left_dir;
7184     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7185       MovDir[x][y] = right_dir;
7186
7187     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7188       MovDelay[x][y] = 9;
7189     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7190       MovDelay[x][y] = 1;
7191   }
7192   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7193   {
7194     TestIfBadThingTouchesOtherBadThing(x, y);
7195
7196     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7197       MovDir[x][y] = left_dir;
7198     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7199       MovDir[x][y] = right_dir;
7200
7201     if (MovDir[x][y] != old_move_dir)
7202       MovDelay[x][y] = 9;
7203   }
7204   else if (element == EL_YAMYAM)
7205   {
7206     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7207     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7208
7209     if (can_turn_left && can_turn_right)
7210       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7211     else if (can_turn_left)
7212       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7213     else if (can_turn_right)
7214       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7215     else
7216       MovDir[x][y] = back_dir;
7217
7218     MovDelay[x][y] = 16 + 16 * RND(3);
7219   }
7220   else if (element == EL_DARK_YAMYAM)
7221   {
7222     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7223                                                          left_x, left_y);
7224     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7225                                                          right_x, right_y);
7226
7227     if (can_turn_left && can_turn_right)
7228       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7229     else if (can_turn_left)
7230       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7231     else if (can_turn_right)
7232       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7233     else
7234       MovDir[x][y] = back_dir;
7235
7236     MovDelay[x][y] = 16 + 16 * RND(3);
7237   }
7238   else if (element == EL_PACMAN)
7239   {
7240     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7241     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7242
7243     if (can_turn_left && can_turn_right)
7244       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7245     else if (can_turn_left)
7246       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7247     else if (can_turn_right)
7248       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7249     else
7250       MovDir[x][y] = back_dir;
7251
7252     MovDelay[x][y] = 6 + RND(40);
7253   }
7254   else if (element == EL_PIG)
7255   {
7256     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7257     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7258     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7259     boolean should_turn_left, should_turn_right, should_move_on;
7260     int rnd_value = 24;
7261     int rnd = RND(rnd_value);
7262
7263     should_turn_left = (can_turn_left &&
7264                         (!can_move_on ||
7265                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7266                                                    y + back_dy + left_dy)));
7267     should_turn_right = (can_turn_right &&
7268                          (!can_move_on ||
7269                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7270                                                     y + back_dy + right_dy)));
7271     should_move_on = (can_move_on &&
7272                       (!can_turn_left ||
7273                        !can_turn_right ||
7274                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7275                                                  y + move_dy + left_dy) ||
7276                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7277                                                  y + move_dy + right_dy)));
7278
7279     if (should_turn_left || should_turn_right || should_move_on)
7280     {
7281       if (should_turn_left && should_turn_right && should_move_on)
7282         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7283                         rnd < 2 * rnd_value / 3 ? right_dir :
7284                         old_move_dir);
7285       else if (should_turn_left && should_turn_right)
7286         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7287       else if (should_turn_left && should_move_on)
7288         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7289       else if (should_turn_right && should_move_on)
7290         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7291       else if (should_turn_left)
7292         MovDir[x][y] = left_dir;
7293       else if (should_turn_right)
7294         MovDir[x][y] = right_dir;
7295       else if (should_move_on)
7296         MovDir[x][y] = old_move_dir;
7297     }
7298     else if (can_move_on && rnd > rnd_value / 8)
7299       MovDir[x][y] = old_move_dir;
7300     else if (can_turn_left && can_turn_right)
7301       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7302     else if (can_turn_left && rnd > rnd_value / 8)
7303       MovDir[x][y] = left_dir;
7304     else if (can_turn_right && rnd > rnd_value/8)
7305       MovDir[x][y] = right_dir;
7306     else
7307       MovDir[x][y] = back_dir;
7308
7309     xx = x + move_xy[MovDir[x][y]].dx;
7310     yy = y + move_xy[MovDir[x][y]].dy;
7311
7312     if (!IN_LEV_FIELD(xx, yy) ||
7313         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7314       MovDir[x][y] = old_move_dir;
7315
7316     MovDelay[x][y] = 0;
7317   }
7318   else if (element == EL_DRAGON)
7319   {
7320     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7321     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7322     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7323     int rnd_value = 24;
7324     int rnd = RND(rnd_value);
7325
7326     if (can_move_on && rnd > rnd_value / 8)
7327       MovDir[x][y] = old_move_dir;
7328     else if (can_turn_left && can_turn_right)
7329       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7330     else if (can_turn_left && rnd > rnd_value / 8)
7331       MovDir[x][y] = left_dir;
7332     else if (can_turn_right && rnd > rnd_value / 8)
7333       MovDir[x][y] = right_dir;
7334     else
7335       MovDir[x][y] = back_dir;
7336
7337     xx = x + move_xy[MovDir[x][y]].dx;
7338     yy = y + move_xy[MovDir[x][y]].dy;
7339
7340     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7341       MovDir[x][y] = old_move_dir;
7342
7343     MovDelay[x][y] = 0;
7344   }
7345   else if (element == EL_MOLE)
7346   {
7347     boolean can_move_on =
7348       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7349                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7350                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7351     if (!can_move_on)
7352     {
7353       boolean can_turn_left =
7354         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7355                               IS_AMOEBOID(Feld[left_x][left_y])));
7356
7357       boolean can_turn_right =
7358         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7359                               IS_AMOEBOID(Feld[right_x][right_y])));
7360
7361       if (can_turn_left && can_turn_right)
7362         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7363       else if (can_turn_left)
7364         MovDir[x][y] = left_dir;
7365       else
7366         MovDir[x][y] = right_dir;
7367     }
7368
7369     if (MovDir[x][y] != old_move_dir)
7370       MovDelay[x][y] = 9;
7371   }
7372   else if (element == EL_BALLOON)
7373   {
7374     MovDir[x][y] = game.wind_direction;
7375     MovDelay[x][y] = 0;
7376   }
7377   else if (element == EL_SPRING)
7378   {
7379 #if USE_NEW_SPRING_BUMPER
7380     if (MovDir[x][y] & MV_HORIZONTAL)
7381     {
7382       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7383           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7384       {
7385         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7386         ResetGfxAnimation(move_x, move_y);
7387         TEST_DrawLevelField(move_x, move_y);
7388
7389         MovDir[x][y] = back_dir;
7390       }
7391       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7392                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7393         MovDir[x][y] = MV_NONE;
7394     }
7395 #else
7396     if (MovDir[x][y] & MV_HORIZONTAL &&
7397         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7398          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7399       MovDir[x][y] = MV_NONE;
7400 #endif
7401
7402     MovDelay[x][y] = 0;
7403   }
7404   else if (element == EL_ROBOT ||
7405            element == EL_SATELLITE ||
7406            element == EL_PENGUIN ||
7407            element == EL_EMC_ANDROID)
7408   {
7409     int attr_x = -1, attr_y = -1;
7410
7411     if (AllPlayersGone)
7412     {
7413       attr_x = ExitX;
7414       attr_y = ExitY;
7415     }
7416     else
7417     {
7418       int i;
7419
7420       for (i = 0; i < MAX_PLAYERS; i++)
7421       {
7422         struct PlayerInfo *player = &stored_player[i];
7423         int jx = player->jx, jy = player->jy;
7424
7425         if (!player->active)
7426           continue;
7427
7428         if (attr_x == -1 ||
7429             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7430         {
7431           attr_x = jx;
7432           attr_y = jy;
7433         }
7434       }
7435     }
7436
7437     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7438         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7439          game.engine_version < VERSION_IDENT(3,1,0,0)))
7440     {
7441       attr_x = ZX;
7442       attr_y = ZY;
7443     }
7444
7445     if (element == EL_PENGUIN)
7446     {
7447       int i;
7448       static int xy[4][2] =
7449       {
7450         { 0, -1 },
7451         { -1, 0 },
7452         { +1, 0 },
7453         { 0, +1 }
7454       };
7455
7456       for (i = 0; i < NUM_DIRECTIONS; i++)
7457       {
7458         int ex = x + xy[i][0];
7459         int ey = y + xy[i][1];
7460
7461         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7462                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7463                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7464                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7465         {
7466           attr_x = ex;
7467           attr_y = ey;
7468           break;
7469         }
7470       }
7471     }
7472
7473     MovDir[x][y] = MV_NONE;
7474     if (attr_x < x)
7475       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7476     else if (attr_x > x)
7477       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7478     if (attr_y < y)
7479       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7480     else if (attr_y > y)
7481       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7482
7483     if (element == EL_ROBOT)
7484     {
7485       int newx, newy;
7486
7487       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7488         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7489       Moving2Blocked(x, y, &newx, &newy);
7490
7491       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7492         MovDelay[x][y] = 8 + 8 * !RND(3);
7493       else
7494         MovDelay[x][y] = 16;
7495     }
7496     else if (element == EL_PENGUIN)
7497     {
7498       int newx, newy;
7499
7500       MovDelay[x][y] = 1;
7501
7502       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7503       {
7504         boolean first_horiz = RND(2);
7505         int new_move_dir = MovDir[x][y];
7506
7507         MovDir[x][y] =
7508           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509         Moving2Blocked(x, y, &newx, &newy);
7510
7511         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7512           return;
7513
7514         MovDir[x][y] =
7515           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7516         Moving2Blocked(x, y, &newx, &newy);
7517
7518         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7519           return;
7520
7521         MovDir[x][y] = old_move_dir;
7522         return;
7523       }
7524     }
7525     else if (element == EL_SATELLITE)
7526     {
7527       int newx, newy;
7528
7529       MovDelay[x][y] = 1;
7530
7531       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7532       {
7533         boolean first_horiz = RND(2);
7534         int new_move_dir = MovDir[x][y];
7535
7536         MovDir[x][y] =
7537           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7538         Moving2Blocked(x, y, &newx, &newy);
7539
7540         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7541           return;
7542
7543         MovDir[x][y] =
7544           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7545         Moving2Blocked(x, y, &newx, &newy);
7546
7547         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7548           return;
7549
7550         MovDir[x][y] = old_move_dir;
7551         return;
7552       }
7553     }
7554     else if (element == EL_EMC_ANDROID)
7555     {
7556       static int check_pos[16] =
7557       {
7558         -1,             /*  0 => (invalid)          */
7559         7,              /*  1 => MV_LEFT            */
7560         3,              /*  2 => MV_RIGHT           */
7561         -1,             /*  3 => (invalid)          */
7562         1,              /*  4 =>            MV_UP   */
7563         0,              /*  5 => MV_LEFT  | MV_UP   */
7564         2,              /*  6 => MV_RIGHT | MV_UP   */
7565         -1,             /*  7 => (invalid)          */
7566         5,              /*  8 =>            MV_DOWN */
7567         6,              /*  9 => MV_LEFT  | MV_DOWN */
7568         4,              /* 10 => MV_RIGHT | MV_DOWN */
7569         -1,             /* 11 => (invalid)          */
7570         -1,             /* 12 => (invalid)          */
7571         -1,             /* 13 => (invalid)          */
7572         -1,             /* 14 => (invalid)          */
7573         -1,             /* 15 => (invalid)          */
7574       };
7575       static struct
7576       {
7577         int dx, dy;
7578         int dir;
7579       } check_xy[8] =
7580       {
7581         { -1, -1,       MV_LEFT  | MV_UP   },
7582         {  0, -1,                  MV_UP   },
7583         { +1, -1,       MV_RIGHT | MV_UP   },
7584         { +1,  0,       MV_RIGHT           },
7585         { +1, +1,       MV_RIGHT | MV_DOWN },
7586         {  0, +1,                  MV_DOWN },
7587         { -1, +1,       MV_LEFT  | MV_DOWN },
7588         { -1,  0,       MV_LEFT            },
7589       };
7590       int start_pos, check_order;
7591       boolean can_clone = FALSE;
7592       int i;
7593
7594       /* check if there is any free field around current position */
7595       for (i = 0; i < 8; i++)
7596       {
7597         int newx = x + check_xy[i].dx;
7598         int newy = y + check_xy[i].dy;
7599
7600         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7601         {
7602           can_clone = TRUE;
7603
7604           break;
7605         }
7606       }
7607
7608       if (can_clone)            /* randomly find an element to clone */
7609       {
7610         can_clone = FALSE;
7611
7612         start_pos = check_pos[RND(8)];
7613         check_order = (RND(2) ? -1 : +1);
7614
7615         for (i = 0; i < 8; i++)
7616         {
7617           int pos_raw = start_pos + i * check_order;
7618           int pos = (pos_raw + 8) % 8;
7619           int newx = x + check_xy[pos].dx;
7620           int newy = y + check_xy[pos].dy;
7621
7622           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7623           {
7624             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7625             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7626
7627             Store[x][y] = Feld[newx][newy];
7628
7629             can_clone = TRUE;
7630
7631             break;
7632           }
7633         }
7634       }
7635
7636       if (can_clone)            /* randomly find a direction to move */
7637       {
7638         can_clone = FALSE;
7639
7640         start_pos = check_pos[RND(8)];
7641         check_order = (RND(2) ? -1 : +1);
7642
7643         for (i = 0; i < 8; i++)
7644         {
7645           int pos_raw = start_pos + i * check_order;
7646           int pos = (pos_raw + 8) % 8;
7647           int newx = x + check_xy[pos].dx;
7648           int newy = y + check_xy[pos].dy;
7649           int new_move_dir = check_xy[pos].dir;
7650
7651           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7652           {
7653             MovDir[x][y] = new_move_dir;
7654             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7655
7656             can_clone = TRUE;
7657
7658             break;
7659           }
7660         }
7661       }
7662
7663       if (can_clone)            /* cloning and moving successful */
7664         return;
7665
7666       /* cannot clone -- try to move towards player */
7667
7668       start_pos = check_pos[MovDir[x][y] & 0x0f];
7669       check_order = (RND(2) ? -1 : +1);
7670
7671       for (i = 0; i < 3; i++)
7672       {
7673         /* first check start_pos, then previous/next or (next/previous) pos */
7674         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7675         int pos = (pos_raw + 8) % 8;
7676         int newx = x + check_xy[pos].dx;
7677         int newy = y + check_xy[pos].dy;
7678         int new_move_dir = check_xy[pos].dir;
7679
7680         if (IS_PLAYER(newx, newy))
7681           break;
7682
7683         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7684         {
7685           MovDir[x][y] = new_move_dir;
7686           MovDelay[x][y] = level.android_move_time * 8 + 1;
7687
7688           break;
7689         }
7690       }
7691     }
7692   }
7693   else if (move_pattern == MV_TURNING_LEFT ||
7694            move_pattern == MV_TURNING_RIGHT ||
7695            move_pattern == MV_TURNING_LEFT_RIGHT ||
7696            move_pattern == MV_TURNING_RIGHT_LEFT ||
7697            move_pattern == MV_TURNING_RANDOM ||
7698            move_pattern == MV_ALL_DIRECTIONS)
7699   {
7700     boolean can_turn_left =
7701       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7702     boolean can_turn_right =
7703       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7704
7705     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7706       return;
7707
7708     if (move_pattern == MV_TURNING_LEFT)
7709       MovDir[x][y] = left_dir;
7710     else if (move_pattern == MV_TURNING_RIGHT)
7711       MovDir[x][y] = right_dir;
7712     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7713       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7714     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7715       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7716     else if (move_pattern == MV_TURNING_RANDOM)
7717       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7718                       can_turn_right && !can_turn_left ? right_dir :
7719                       RND(2) ? left_dir : right_dir);
7720     else if (can_turn_left && can_turn_right)
7721       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7722     else if (can_turn_left)
7723       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7724     else if (can_turn_right)
7725       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7726     else
7727       MovDir[x][y] = back_dir;
7728
7729     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7730   }
7731   else if (move_pattern == MV_HORIZONTAL ||
7732            move_pattern == MV_VERTICAL)
7733   {
7734     if (move_pattern & old_move_dir)
7735       MovDir[x][y] = back_dir;
7736     else if (move_pattern == MV_HORIZONTAL)
7737       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7738     else if (move_pattern == MV_VERTICAL)
7739       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7740
7741     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7742   }
7743   else if (move_pattern & MV_ANY_DIRECTION)
7744   {
7745     MovDir[x][y] = move_pattern;
7746     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7747   }
7748   else if (move_pattern & MV_WIND_DIRECTION)
7749   {
7750     MovDir[x][y] = game.wind_direction;
7751     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7752   }
7753   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7754   {
7755     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7756       MovDir[x][y] = left_dir;
7757     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7758       MovDir[x][y] = right_dir;
7759
7760     if (MovDir[x][y] != old_move_dir)
7761       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7762   }
7763   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7764   {
7765     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7766       MovDir[x][y] = right_dir;
7767     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7768       MovDir[x][y] = left_dir;
7769
7770     if (MovDir[x][y] != old_move_dir)
7771       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7772   }
7773   else if (move_pattern == MV_TOWARDS_PLAYER ||
7774            move_pattern == MV_AWAY_FROM_PLAYER)
7775   {
7776     int attr_x = -1, attr_y = -1;
7777     int newx, newy;
7778     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7779
7780     if (AllPlayersGone)
7781     {
7782       attr_x = ExitX;
7783       attr_y = ExitY;
7784     }
7785     else
7786     {
7787       int i;
7788
7789       for (i = 0; i < MAX_PLAYERS; i++)
7790       {
7791         struct PlayerInfo *player = &stored_player[i];
7792         int jx = player->jx, jy = player->jy;
7793
7794         if (!player->active)
7795           continue;
7796
7797         if (attr_x == -1 ||
7798             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7799         {
7800           attr_x = jx;
7801           attr_y = jy;
7802         }
7803       }
7804     }
7805
7806     MovDir[x][y] = MV_NONE;
7807     if (attr_x < x)
7808       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7809     else if (attr_x > x)
7810       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7811     if (attr_y < y)
7812       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7813     else if (attr_y > y)
7814       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7815
7816     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7817
7818     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7819     {
7820       boolean first_horiz = RND(2);
7821       int new_move_dir = MovDir[x][y];
7822
7823       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7824       {
7825         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7826         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7827
7828         return;
7829       }
7830
7831       MovDir[x][y] =
7832         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7833       Moving2Blocked(x, y, &newx, &newy);
7834
7835       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7836         return;
7837
7838       MovDir[x][y] =
7839         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7840       Moving2Blocked(x, y, &newx, &newy);
7841
7842       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7843         return;
7844
7845       MovDir[x][y] = old_move_dir;
7846     }
7847   }
7848   else if (move_pattern == MV_WHEN_PUSHED ||
7849            move_pattern == MV_WHEN_DROPPED)
7850   {
7851     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7852       MovDir[x][y] = MV_NONE;
7853
7854     MovDelay[x][y] = 0;
7855   }
7856   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7857   {
7858     static int test_xy[7][2] =
7859     {
7860       { 0, -1 },
7861       { -1, 0 },
7862       { +1, 0 },
7863       { 0, +1 },
7864       { 0, -1 },
7865       { -1, 0 },
7866       { +1, 0 },
7867     };
7868     static int test_dir[7] =
7869     {
7870       MV_UP,
7871       MV_LEFT,
7872       MV_RIGHT,
7873       MV_DOWN,
7874       MV_UP,
7875       MV_LEFT,
7876       MV_RIGHT,
7877     };
7878     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7879     int move_preference = -1000000;     /* start with very low preference */
7880     int new_move_dir = MV_NONE;
7881     int start_test = RND(4);
7882     int i;
7883
7884     for (i = 0; i < NUM_DIRECTIONS; i++)
7885     {
7886       int move_dir = test_dir[start_test + i];
7887       int move_dir_preference;
7888
7889       xx = x + test_xy[start_test + i][0];
7890       yy = y + test_xy[start_test + i][1];
7891
7892       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7893           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7894       {
7895         new_move_dir = move_dir;
7896
7897         break;
7898       }
7899
7900       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7901         continue;
7902
7903       move_dir_preference = -1 * RunnerVisit[xx][yy];
7904       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7905         move_dir_preference = PlayerVisit[xx][yy];
7906
7907       if (move_dir_preference > move_preference)
7908       {
7909         /* prefer field that has not been visited for the longest time */
7910         move_preference = move_dir_preference;
7911         new_move_dir = move_dir;
7912       }
7913       else if (move_dir_preference == move_preference &&
7914                move_dir == old_move_dir)
7915       {
7916         /* prefer last direction when all directions are preferred equally */
7917         move_preference = move_dir_preference;
7918         new_move_dir = move_dir;
7919       }
7920     }
7921
7922     MovDir[x][y] = new_move_dir;
7923     if (old_move_dir != new_move_dir)
7924       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7925   }
7926 }
7927
7928 static void TurnRound(int x, int y)
7929 {
7930   int direction = MovDir[x][y];
7931
7932   TurnRoundExt(x, y);
7933
7934   GfxDir[x][y] = MovDir[x][y];
7935
7936   if (direction != MovDir[x][y])
7937     GfxFrame[x][y] = 0;
7938
7939   if (MovDelay[x][y])
7940     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7941
7942   ResetGfxFrame(x, y, FALSE);
7943 }
7944
7945 static boolean JustBeingPushed(int x, int y)
7946 {
7947   int i;
7948
7949   for (i = 0; i < MAX_PLAYERS; i++)
7950   {
7951     struct PlayerInfo *player = &stored_player[i];
7952
7953     if (player->active && player->is_pushing && player->MovPos)
7954     {
7955       int next_jx = player->jx + (player->jx - player->last_jx);
7956       int next_jy = player->jy + (player->jy - player->last_jy);
7957
7958       if (x == next_jx && y == next_jy)
7959         return TRUE;
7960     }
7961   }
7962
7963   return FALSE;
7964 }
7965
7966 void StartMoving(int x, int y)
7967 {
7968   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7969   int element = Feld[x][y];
7970
7971   if (Stop[x][y])
7972     return;
7973
7974   if (MovDelay[x][y] == 0)
7975     GfxAction[x][y] = ACTION_DEFAULT;
7976
7977   if (CAN_FALL(element) && y < lev_fieldy - 1)
7978   {
7979     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7980         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7981       if (JustBeingPushed(x, y))
7982         return;
7983
7984     if (element == EL_QUICKSAND_FULL)
7985     {
7986       if (IS_FREE(x, y + 1))
7987       {
7988         InitMovingField(x, y, MV_DOWN);
7989         started_moving = TRUE;
7990
7991         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7992 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7993         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7994           Store[x][y] = EL_ROCK;
7995 #else
7996         Store[x][y] = EL_ROCK;
7997 #endif
7998
7999         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8000       }
8001       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8002       {
8003         if (!MovDelay[x][y])
8004         {
8005           MovDelay[x][y] = TILEY + 1;
8006
8007           ResetGfxAnimation(x, y);
8008           ResetGfxAnimation(x, y + 1);
8009         }
8010
8011         if (MovDelay[x][y])
8012         {
8013           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8014           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8015
8016           MovDelay[x][y]--;
8017           if (MovDelay[x][y])
8018             return;
8019         }
8020
8021         Feld[x][y] = EL_QUICKSAND_EMPTY;
8022         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8023         Store[x][y + 1] = Store[x][y];
8024         Store[x][y] = 0;
8025
8026         PlayLevelSoundAction(x, y, ACTION_FILLING);
8027       }
8028       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8029       {
8030         if (!MovDelay[x][y])
8031         {
8032           MovDelay[x][y] = TILEY + 1;
8033
8034           ResetGfxAnimation(x, y);
8035           ResetGfxAnimation(x, y + 1);
8036         }
8037
8038         if (MovDelay[x][y])
8039         {
8040           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8041           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8042
8043           MovDelay[x][y]--;
8044           if (MovDelay[x][y])
8045             return;
8046         }
8047
8048         Feld[x][y] = EL_QUICKSAND_EMPTY;
8049         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8050         Store[x][y + 1] = Store[x][y];
8051         Store[x][y] = 0;
8052
8053         PlayLevelSoundAction(x, y, ACTION_FILLING);
8054       }
8055     }
8056     else if (element == EL_QUICKSAND_FAST_FULL)
8057     {
8058       if (IS_FREE(x, y + 1))
8059       {
8060         InitMovingField(x, y, MV_DOWN);
8061         started_moving = TRUE;
8062
8063         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8064 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8065         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8066           Store[x][y] = EL_ROCK;
8067 #else
8068         Store[x][y] = EL_ROCK;
8069 #endif
8070
8071         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8072       }
8073       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8074       {
8075         if (!MovDelay[x][y])
8076         {
8077           MovDelay[x][y] = TILEY + 1;
8078
8079           ResetGfxAnimation(x, y);
8080           ResetGfxAnimation(x, y + 1);
8081         }
8082
8083         if (MovDelay[x][y])
8084         {
8085           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8086           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8087
8088           MovDelay[x][y]--;
8089           if (MovDelay[x][y])
8090             return;
8091         }
8092
8093         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8094         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8095         Store[x][y + 1] = Store[x][y];
8096         Store[x][y] = 0;
8097
8098         PlayLevelSoundAction(x, y, ACTION_FILLING);
8099       }
8100       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8101       {
8102         if (!MovDelay[x][y])
8103         {
8104           MovDelay[x][y] = TILEY + 1;
8105
8106           ResetGfxAnimation(x, y);
8107           ResetGfxAnimation(x, y + 1);
8108         }
8109
8110         if (MovDelay[x][y])
8111         {
8112           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8113           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8114
8115           MovDelay[x][y]--;
8116           if (MovDelay[x][y])
8117             return;
8118         }
8119
8120         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8121         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8122         Store[x][y + 1] = Store[x][y];
8123         Store[x][y] = 0;
8124
8125         PlayLevelSoundAction(x, y, ACTION_FILLING);
8126       }
8127     }
8128     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8129              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8130     {
8131       InitMovingField(x, y, MV_DOWN);
8132       started_moving = TRUE;
8133
8134       Feld[x][y] = EL_QUICKSAND_FILLING;
8135       Store[x][y] = element;
8136
8137       PlayLevelSoundAction(x, y, ACTION_FILLING);
8138     }
8139     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8140              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8141     {
8142       InitMovingField(x, y, MV_DOWN);
8143       started_moving = TRUE;
8144
8145       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8146       Store[x][y] = element;
8147
8148       PlayLevelSoundAction(x, y, ACTION_FILLING);
8149     }
8150     else if (element == EL_MAGIC_WALL_FULL)
8151     {
8152       if (IS_FREE(x, y + 1))
8153       {
8154         InitMovingField(x, y, MV_DOWN);
8155         started_moving = TRUE;
8156
8157         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8158         Store[x][y] = EL_CHANGED(Store[x][y]);
8159       }
8160       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8161       {
8162         if (!MovDelay[x][y])
8163           MovDelay[x][y] = TILEY / 4 + 1;
8164
8165         if (MovDelay[x][y])
8166         {
8167           MovDelay[x][y]--;
8168           if (MovDelay[x][y])
8169             return;
8170         }
8171
8172         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8173         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8174         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8175         Store[x][y] = 0;
8176       }
8177     }
8178     else if (element == EL_BD_MAGIC_WALL_FULL)
8179     {
8180       if (IS_FREE(x, y + 1))
8181       {
8182         InitMovingField(x, y, MV_DOWN);
8183         started_moving = TRUE;
8184
8185         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8186         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8187       }
8188       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8189       {
8190         if (!MovDelay[x][y])
8191           MovDelay[x][y] = TILEY / 4 + 1;
8192
8193         if (MovDelay[x][y])
8194         {
8195           MovDelay[x][y]--;
8196           if (MovDelay[x][y])
8197             return;
8198         }
8199
8200         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8201         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8202         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8203         Store[x][y] = 0;
8204       }
8205     }
8206     else if (element == EL_DC_MAGIC_WALL_FULL)
8207     {
8208       if (IS_FREE(x, y + 1))
8209       {
8210         InitMovingField(x, y, MV_DOWN);
8211         started_moving = TRUE;
8212
8213         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8214         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8215       }
8216       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8217       {
8218         if (!MovDelay[x][y])
8219           MovDelay[x][y] = TILEY / 4 + 1;
8220
8221         if (MovDelay[x][y])
8222         {
8223           MovDelay[x][y]--;
8224           if (MovDelay[x][y])
8225             return;
8226         }
8227
8228         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8229         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8230         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8231         Store[x][y] = 0;
8232       }
8233     }
8234     else if ((CAN_PASS_MAGIC_WALL(element) &&
8235               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8236                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8237              (CAN_PASS_DC_MAGIC_WALL(element) &&
8238               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8239
8240     {
8241       InitMovingField(x, y, MV_DOWN);
8242       started_moving = TRUE;
8243
8244       Feld[x][y] =
8245         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8246          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8247          EL_DC_MAGIC_WALL_FILLING);
8248       Store[x][y] = element;
8249     }
8250     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8251     {
8252       SplashAcid(x, y + 1);
8253
8254       InitMovingField(x, y, MV_DOWN);
8255       started_moving = TRUE;
8256
8257       Store[x][y] = EL_ACID;
8258     }
8259     else if (
8260 #if USE_FIX_IMPACT_COLLISION
8261              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8262               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8263 #else
8264              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8265               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8266 #endif
8267              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8268               CAN_FALL(element) && WasJustFalling[x][y] &&
8269               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8270
8271              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8272               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8273               (Feld[x][y + 1] == EL_BLOCKED)))
8274     {
8275       /* this is needed for a special case not covered by calling "Impact()"
8276          from "ContinueMoving()": if an element moves to a tile directly below
8277          another element which was just falling on that tile (which was empty
8278          in the previous frame), the falling element above would just stop
8279          instead of smashing the element below (in previous version, the above
8280          element was just checked for "moving" instead of "falling", resulting
8281          in incorrect smashes caused by horizontal movement of the above
8282          element; also, the case of the player being the element to smash was
8283          simply not covered here... :-/ ) */
8284
8285       CheckCollision[x][y] = 0;
8286       CheckImpact[x][y] = 0;
8287
8288       Impact(x, y);
8289     }
8290     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8291     {
8292       if (MovDir[x][y] == MV_NONE)
8293       {
8294         InitMovingField(x, y, MV_DOWN);
8295         started_moving = TRUE;
8296       }
8297     }
8298     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8299     {
8300       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8301         MovDir[x][y] = MV_DOWN;
8302
8303       InitMovingField(x, y, MV_DOWN);
8304       started_moving = TRUE;
8305     }
8306     else if (element == EL_AMOEBA_DROP)
8307     {
8308       Feld[x][y] = EL_AMOEBA_GROWING;
8309       Store[x][y] = EL_AMOEBA_WET;
8310     }
8311     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8312               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8313              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8314              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8315     {
8316       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8317                                 (IS_FREE(x - 1, y + 1) ||
8318                                  Feld[x - 1][y + 1] == EL_ACID));
8319       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8320                                 (IS_FREE(x + 1, y + 1) ||
8321                                  Feld[x + 1][y + 1] == EL_ACID));
8322       boolean can_fall_any  = (can_fall_left || can_fall_right);
8323       boolean can_fall_both = (can_fall_left && can_fall_right);
8324       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8325
8326 #if USE_NEW_ALL_SLIPPERY
8327       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8328       {
8329         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8330           can_fall_right = FALSE;
8331         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8332           can_fall_left = FALSE;
8333         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8334           can_fall_right = FALSE;
8335         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8336           can_fall_left = FALSE;
8337
8338         can_fall_any  = (can_fall_left || can_fall_right);
8339         can_fall_both = FALSE;
8340       }
8341 #else
8342       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8343       {
8344         if (slippery_type == SLIPPERY_ONLY_LEFT)
8345           can_fall_right = FALSE;
8346         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8347           can_fall_left = FALSE;
8348         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8349           can_fall_right = FALSE;
8350         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8351           can_fall_left = FALSE;
8352
8353         can_fall_any  = (can_fall_left || can_fall_right);
8354         can_fall_both = (can_fall_left && can_fall_right);
8355       }
8356 #endif
8357
8358 #if USE_NEW_ALL_SLIPPERY
8359 #else
8360 #if USE_NEW_SP_SLIPPERY
8361       /* !!! better use the same properties as for custom elements here !!! */
8362       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8363                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8364       {
8365         can_fall_right = FALSE;         /* slip down on left side */
8366         can_fall_both = FALSE;
8367       }
8368 #endif
8369 #endif
8370
8371 #if USE_NEW_ALL_SLIPPERY
8372       if (can_fall_both)
8373       {
8374         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8375           can_fall_right = FALSE;       /* slip down on left side */
8376         else
8377           can_fall_left = !(can_fall_right = RND(2));
8378
8379         can_fall_both = FALSE;
8380       }
8381 #else
8382       if (can_fall_both)
8383       {
8384         if (game.emulation == EMU_BOULDERDASH ||
8385             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8386           can_fall_right = FALSE;       /* slip down on left side */
8387         else
8388           can_fall_left = !(can_fall_right = RND(2));
8389
8390         can_fall_both = FALSE;
8391       }
8392 #endif
8393
8394       if (can_fall_any)
8395       {
8396         /* if not determined otherwise, prefer left side for slipping down */
8397         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8398         started_moving = TRUE;
8399       }
8400     }
8401 #if 0
8402     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8403 #else
8404     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8405 #endif
8406     {
8407       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8408       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8409       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8410       int belt_dir = game.belt_dir[belt_nr];
8411
8412       if ((belt_dir == MV_LEFT  && left_is_free) ||
8413           (belt_dir == MV_RIGHT && right_is_free))
8414       {
8415         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8416
8417         InitMovingField(x, y, belt_dir);
8418         started_moving = TRUE;
8419
8420         Pushed[x][y] = TRUE;
8421         Pushed[nextx][y] = TRUE;
8422
8423         GfxAction[x][y] = ACTION_DEFAULT;
8424       }
8425       else
8426       {
8427         MovDir[x][y] = 0;       /* if element was moving, stop it */
8428       }
8429     }
8430   }
8431
8432   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8433 #if 0
8434   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8435 #else
8436   if (CAN_MOVE(element) && !started_moving)
8437 #endif
8438   {
8439     int move_pattern = element_info[element].move_pattern;
8440     int newx, newy;
8441
8442 #if 0
8443 #if DEBUG
8444     if (MovDir[x][y] == MV_NONE)
8445     {
8446       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8447              x, y, element, element_info[element].token_name);
8448       printf("StartMoving(): This should never happen!\n");
8449     }
8450 #endif
8451 #endif
8452
8453     Moving2Blocked(x, y, &newx, &newy);
8454
8455     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8456       return;
8457
8458     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8459         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8460     {
8461       WasJustMoving[x][y] = 0;
8462       CheckCollision[x][y] = 0;
8463
8464       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8465
8466       if (Feld[x][y] != element)        /* element has changed */
8467         return;
8468     }
8469
8470     if (!MovDelay[x][y])        /* start new movement phase */
8471     {
8472       /* all objects that can change their move direction after each step
8473          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8474
8475       if (element != EL_YAMYAM &&
8476           element != EL_DARK_YAMYAM &&
8477           element != EL_PACMAN &&
8478           !(move_pattern & MV_ANY_DIRECTION) &&
8479           move_pattern != MV_TURNING_LEFT &&
8480           move_pattern != MV_TURNING_RIGHT &&
8481           move_pattern != MV_TURNING_LEFT_RIGHT &&
8482           move_pattern != MV_TURNING_RIGHT_LEFT &&
8483           move_pattern != MV_TURNING_RANDOM)
8484       {
8485         TurnRound(x, y);
8486
8487         if (MovDelay[x][y] && (element == EL_BUG ||
8488                                element == EL_SPACESHIP ||
8489                                element == EL_SP_SNIKSNAK ||
8490                                element == EL_SP_ELECTRON ||
8491                                element == EL_MOLE))
8492           TEST_DrawLevelField(x, y);
8493       }
8494     }
8495
8496     if (MovDelay[x][y])         /* wait some time before next movement */
8497     {
8498       MovDelay[x][y]--;
8499
8500       if (element == EL_ROBOT ||
8501           element == EL_YAMYAM ||
8502           element == EL_DARK_YAMYAM)
8503       {
8504         DrawLevelElementAnimationIfNeeded(x, y, element);
8505         PlayLevelSoundAction(x, y, ACTION_WAITING);
8506       }
8507       else if (element == EL_SP_ELECTRON)
8508         DrawLevelElementAnimationIfNeeded(x, y, element);
8509       else if (element == EL_DRAGON)
8510       {
8511         int i;
8512         int dir = MovDir[x][y];
8513         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8514         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8515         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8516                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8517                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8518                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8519         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8520
8521         GfxAction[x][y] = ACTION_ATTACKING;
8522
8523         if (IS_PLAYER(x, y))
8524           DrawPlayerField(x, y);
8525         else
8526           TEST_DrawLevelField(x, y);
8527
8528         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8529
8530         for (i = 1; i <= 3; i++)
8531         {
8532           int xx = x + i * dx;
8533           int yy = y + i * dy;
8534           int sx = SCREENX(xx);
8535           int sy = SCREENY(yy);
8536           int flame_graphic = graphic + (i - 1);
8537
8538           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8539             break;
8540
8541           if (MovDelay[x][y])
8542           {
8543             int flamed = MovingOrBlocked2Element(xx, yy);
8544
8545             /* !!! */
8546 #if 0
8547             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8548               Bang(xx, yy);
8549             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8550               RemoveMovingField(xx, yy);
8551             else
8552               RemoveField(xx, yy);
8553 #else
8554             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8555               Bang(xx, yy);
8556             else
8557               RemoveMovingField(xx, yy);
8558 #endif
8559
8560             ChangeDelay[xx][yy] = 0;
8561
8562             Feld[xx][yy] = EL_FLAMES;
8563
8564             if (IN_SCR_FIELD(sx, sy))
8565             {
8566               TEST_DrawLevelFieldCrumbled(xx, yy);
8567               DrawGraphic(sx, sy, flame_graphic, frame);
8568             }
8569           }
8570           else
8571           {
8572             if (Feld[xx][yy] == EL_FLAMES)
8573               Feld[xx][yy] = EL_EMPTY;
8574             TEST_DrawLevelField(xx, yy);
8575           }
8576         }
8577       }
8578
8579       if (MovDelay[x][y])       /* element still has to wait some time */
8580       {
8581         PlayLevelSoundAction(x, y, ACTION_WAITING);
8582
8583         return;
8584       }
8585     }
8586
8587     /* now make next step */
8588
8589     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8590
8591     if (DONT_COLLIDE_WITH(element) &&
8592         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8593         !PLAYER_ENEMY_PROTECTED(newx, newy))
8594     {
8595       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8596
8597       return;
8598     }
8599
8600     else if (CAN_MOVE_INTO_ACID(element) &&
8601              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8602              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8603              (MovDir[x][y] == MV_DOWN ||
8604               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8605     {
8606       SplashAcid(newx, newy);
8607       Store[x][y] = EL_ACID;
8608     }
8609     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8610     {
8611       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8612           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8613           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8614           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8615       {
8616         RemoveField(x, y);
8617         TEST_DrawLevelField(x, y);
8618
8619         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8620         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8621           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8622
8623         local_player->friends_still_needed--;
8624         if (!local_player->friends_still_needed &&
8625             !local_player->GameOver && AllPlayersGone)
8626           PlayerWins(local_player);
8627
8628         return;
8629       }
8630       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8631       {
8632         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8633           TEST_DrawLevelField(newx, newy);
8634         else
8635           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8636       }
8637       else if (!IS_FREE(newx, newy))
8638       {
8639         GfxAction[x][y] = ACTION_WAITING;
8640
8641         if (IS_PLAYER(x, y))
8642           DrawPlayerField(x, y);
8643         else
8644           TEST_DrawLevelField(x, y);
8645
8646         return;
8647       }
8648     }
8649     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8650     {
8651       if (IS_FOOD_PIG(Feld[newx][newy]))
8652       {
8653         if (IS_MOVING(newx, newy))
8654           RemoveMovingField(newx, newy);
8655         else
8656         {
8657           Feld[newx][newy] = EL_EMPTY;
8658           TEST_DrawLevelField(newx, newy);
8659         }
8660
8661         PlayLevelSound(x, y, SND_PIG_DIGGING);
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665         if (IS_PLAYER(x, y))
8666           DrawPlayerField(x, y);
8667         else
8668           TEST_DrawLevelField(x, y);
8669
8670         return;
8671       }
8672     }
8673     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8674     {
8675       if (Store[x][y] != EL_EMPTY)
8676       {
8677         boolean can_clone = FALSE;
8678         int xx, yy;
8679
8680         /* check if element to clone is still there */
8681         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8682         {
8683           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8684           {
8685             can_clone = TRUE;
8686
8687             break;
8688           }
8689         }
8690
8691         /* cannot clone or target field not free anymore -- do not clone */
8692         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8693           Store[x][y] = EL_EMPTY;
8694       }
8695
8696       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8697       {
8698         if (IS_MV_DIAGONAL(MovDir[x][y]))
8699         {
8700           int diagonal_move_dir = MovDir[x][y];
8701           int stored = Store[x][y];
8702           int change_delay = 8;
8703           int graphic;
8704
8705           /* android is moving diagonally */
8706
8707           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8708
8709           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8710           GfxElement[x][y] = EL_EMC_ANDROID;
8711           GfxAction[x][y] = ACTION_SHRINKING;
8712           GfxDir[x][y] = diagonal_move_dir;
8713           ChangeDelay[x][y] = change_delay;
8714
8715           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8716                                    GfxDir[x][y]);
8717
8718           DrawLevelGraphicAnimation(x, y, graphic);
8719           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8720
8721           if (Feld[newx][newy] == EL_ACID)
8722           {
8723             SplashAcid(newx, newy);
8724
8725             return;
8726           }
8727
8728           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8729
8730           Store[newx][newy] = EL_EMC_ANDROID;
8731           GfxElement[newx][newy] = EL_EMC_ANDROID;
8732           GfxAction[newx][newy] = ACTION_GROWING;
8733           GfxDir[newx][newy] = diagonal_move_dir;
8734           ChangeDelay[newx][newy] = change_delay;
8735
8736           graphic = el_act_dir2img(GfxElement[newx][newy],
8737                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8738
8739           DrawLevelGraphicAnimation(newx, newy, graphic);
8740           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8741
8742           return;
8743         }
8744         else
8745         {
8746           Feld[newx][newy] = EL_EMPTY;
8747           TEST_DrawLevelField(newx, newy);
8748
8749           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8750         }
8751       }
8752       else if (!IS_FREE(newx, newy))
8753       {
8754 #if 0
8755         if (IS_PLAYER(x, y))
8756           DrawPlayerField(x, y);
8757         else
8758           TEST_DrawLevelField(x, y);
8759 #endif
8760
8761         return;
8762       }
8763     }
8764     else if (IS_CUSTOM_ELEMENT(element) &&
8765              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8766     {
8767 #if 1
8768       if (!DigFieldByCE(newx, newy, element))
8769         return;
8770 #else
8771       int new_element = Feld[newx][newy];
8772
8773       if (!IS_FREE(newx, newy))
8774       {
8775         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8776                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8777                       ACTION_BREAKING);
8778
8779         /* no element can dig solid indestructible elements */
8780         if (IS_INDESTRUCTIBLE(new_element) &&
8781             !IS_DIGGABLE(new_element) &&
8782             !IS_COLLECTIBLE(new_element))
8783           return;
8784
8785         if (AmoebaNr[newx][newy] &&
8786             (new_element == EL_AMOEBA_FULL ||
8787              new_element == EL_BD_AMOEBA ||
8788              new_element == EL_AMOEBA_GROWING))
8789         {
8790           AmoebaCnt[AmoebaNr[newx][newy]]--;
8791           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8792         }
8793
8794         if (IS_MOVING(newx, newy))
8795           RemoveMovingField(newx, newy);
8796         else
8797         {
8798           RemoveField(newx, newy);
8799           TEST_DrawLevelField(newx, newy);
8800         }
8801
8802         /* if digged element was about to explode, prevent the explosion */
8803         ExplodeField[newx][newy] = EX_TYPE_NONE;
8804
8805         PlayLevelSoundAction(x, y, action);
8806       }
8807
8808       Store[newx][newy] = EL_EMPTY;
8809
8810 #if 1
8811       /* this makes it possible to leave the removed element again */
8812       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8813         Store[newx][newy] = new_element;
8814 #else
8815       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8816       {
8817         int move_leave_element = element_info[element].move_leave_element;
8818
8819         /* this makes it possible to leave the removed element again */
8820         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8821                              new_element : move_leave_element);
8822       }
8823 #endif
8824
8825 #endif
8826
8827       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8828       {
8829         RunnerVisit[x][y] = FrameCounter;
8830         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8831       }
8832     }
8833     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8834     {
8835       if (!IS_FREE(newx, newy))
8836       {
8837         if (IS_PLAYER(x, y))
8838           DrawPlayerField(x, y);
8839         else
8840           TEST_DrawLevelField(x, y);
8841
8842         return;
8843       }
8844       else
8845       {
8846         boolean wanna_flame = !RND(10);
8847         int dx = newx - x, dy = newy - y;
8848         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8849         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8850         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8851                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8852         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8853                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8854
8855         if ((wanna_flame ||
8856              IS_CLASSIC_ENEMY(element1) ||
8857              IS_CLASSIC_ENEMY(element2)) &&
8858             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8859             element1 != EL_FLAMES && element2 != EL_FLAMES)
8860         {
8861           ResetGfxAnimation(x, y);
8862           GfxAction[x][y] = ACTION_ATTACKING;
8863
8864           if (IS_PLAYER(x, y))
8865             DrawPlayerField(x, y);
8866           else
8867             TEST_DrawLevelField(x, y);
8868
8869           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8870
8871           MovDelay[x][y] = 50;
8872
8873           /* !!! */
8874 #if 0
8875           RemoveField(newx, newy);
8876 #endif
8877           Feld[newx][newy] = EL_FLAMES;
8878           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8879           {
8880 #if 0
8881             RemoveField(newx1, newy1);
8882 #endif
8883             Feld[newx1][newy1] = EL_FLAMES;
8884           }
8885           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8886           {
8887 #if 0
8888             RemoveField(newx2, newy2);
8889 #endif
8890             Feld[newx2][newy2] = EL_FLAMES;
8891           }
8892
8893           return;
8894         }
8895       }
8896     }
8897     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8898              Feld[newx][newy] == EL_DIAMOND)
8899     {
8900       if (IS_MOVING(newx, newy))
8901         RemoveMovingField(newx, newy);
8902       else
8903       {
8904         Feld[newx][newy] = EL_EMPTY;
8905         TEST_DrawLevelField(newx, newy);
8906       }
8907
8908       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8909     }
8910     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8911              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8912     {
8913       if (AmoebaNr[newx][newy])
8914       {
8915         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8916         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8917             Feld[newx][newy] == EL_BD_AMOEBA)
8918           AmoebaCnt[AmoebaNr[newx][newy]]--;
8919       }
8920
8921 #if 0
8922       /* !!! test !!! */
8923       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8924       {
8925         RemoveMovingField(newx, newy);
8926       }
8927 #else
8928       if (IS_MOVING(newx, newy))
8929       {
8930         RemoveMovingField(newx, newy);
8931       }
8932 #endif
8933       else
8934       {
8935         Feld[newx][newy] = EL_EMPTY;
8936         TEST_DrawLevelField(newx, newy);
8937       }
8938
8939       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8940     }
8941     else if ((element == EL_PACMAN || element == EL_MOLE)
8942              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8943     {
8944       if (AmoebaNr[newx][newy])
8945       {
8946         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8947         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8948             Feld[newx][newy] == EL_BD_AMOEBA)
8949           AmoebaCnt[AmoebaNr[newx][newy]]--;
8950       }
8951
8952       if (element == EL_MOLE)
8953       {
8954         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8955         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8956
8957         ResetGfxAnimation(x, y);
8958         GfxAction[x][y] = ACTION_DIGGING;
8959         TEST_DrawLevelField(x, y);
8960
8961         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8962
8963         return;                         /* wait for shrinking amoeba */
8964       }
8965       else      /* element == EL_PACMAN */
8966       {
8967         Feld[newx][newy] = EL_EMPTY;
8968         TEST_DrawLevelField(newx, newy);
8969         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8970       }
8971     }
8972     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8973              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8974               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8975     {
8976       /* wait for shrinking amoeba to completely disappear */
8977       return;
8978     }
8979     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8980     {
8981       /* object was running against a wall */
8982
8983       TurnRound(x, y);
8984
8985 #if 0
8986       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8987       if (move_pattern & MV_ANY_DIRECTION &&
8988           move_pattern == MovDir[x][y])
8989       {
8990         int blocking_element =
8991           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8992
8993         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8994                                  MovDir[x][y]);
8995
8996         element = Feld[x][y];   /* element might have changed */
8997       }
8998 #endif
8999
9000       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9001         DrawLevelElementAnimation(x, y, element);
9002
9003       if (DONT_TOUCH(element))
9004         TestIfBadThingTouchesPlayer(x, y);
9005
9006       return;
9007     }
9008
9009     InitMovingField(x, y, MovDir[x][y]);
9010
9011     PlayLevelSoundAction(x, y, ACTION_MOVING);
9012   }
9013
9014   if (MovDir[x][y])
9015     ContinueMoving(x, y);
9016 }
9017
9018 void ContinueMoving(int x, int y)
9019 {
9020   int element = Feld[x][y];
9021   struct ElementInfo *ei = &element_info[element];
9022   int direction = MovDir[x][y];
9023   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9024   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9025   int newx = x + dx, newy = y + dy;
9026   int stored = Store[x][y];
9027   int stored_new = Store[newx][newy];
9028   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9029   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9030   boolean last_line = (newy == lev_fieldy - 1);
9031
9032   MovPos[x][y] += getElementMoveStepsize(x, y);
9033
9034   if (pushed_by_player) /* special case: moving object pushed by player */
9035     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9036
9037   if (ABS(MovPos[x][y]) < TILEX)
9038   {
9039 #if 0
9040     int ee = Feld[x][y];
9041     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9042     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9043
9044     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9045            x, y, ABS(MovPos[x][y]),
9046            ee, gg, ff,
9047            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9048 #endif
9049
9050     TEST_DrawLevelField(x, y);
9051
9052     return;     /* element is still moving */
9053   }
9054
9055   /* element reached destination field */
9056
9057   Feld[x][y] = EL_EMPTY;
9058   Feld[newx][newy] = element;
9059   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9060
9061   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9062   {
9063     element = Feld[newx][newy] = EL_ACID;
9064   }
9065   else if (element == EL_MOLE)
9066   {
9067     Feld[x][y] = EL_SAND;
9068
9069     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9070   }
9071   else if (element == EL_QUICKSAND_FILLING)
9072   {
9073     element = Feld[newx][newy] = get_next_element(element);
9074     Store[newx][newy] = Store[x][y];
9075   }
9076   else if (element == EL_QUICKSAND_EMPTYING)
9077   {
9078     Feld[x][y] = get_next_element(element);
9079     element = Feld[newx][newy] = Store[x][y];
9080   }
9081   else if (element == EL_QUICKSAND_FAST_FILLING)
9082   {
9083     element = Feld[newx][newy] = get_next_element(element);
9084     Store[newx][newy] = Store[x][y];
9085   }
9086   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9087   {
9088     Feld[x][y] = get_next_element(element);
9089     element = Feld[newx][newy] = Store[x][y];
9090   }
9091   else if (element == EL_MAGIC_WALL_FILLING)
9092   {
9093     element = Feld[newx][newy] = get_next_element(element);
9094     if (!game.magic_wall_active)
9095       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9096     Store[newx][newy] = Store[x][y];
9097   }
9098   else if (element == EL_MAGIC_WALL_EMPTYING)
9099   {
9100     Feld[x][y] = get_next_element(element);
9101     if (!game.magic_wall_active)
9102       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9103     element = Feld[newx][newy] = Store[x][y];
9104
9105 #if USE_NEW_CUSTOM_VALUE
9106     InitField(newx, newy, FALSE);
9107 #endif
9108   }
9109   else if (element == EL_BD_MAGIC_WALL_FILLING)
9110   {
9111     element = Feld[newx][newy] = get_next_element(element);
9112     if (!game.magic_wall_active)
9113       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9114     Store[newx][newy] = Store[x][y];
9115   }
9116   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9117   {
9118     Feld[x][y] = get_next_element(element);
9119     if (!game.magic_wall_active)
9120       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9121     element = Feld[newx][newy] = Store[x][y];
9122
9123 #if USE_NEW_CUSTOM_VALUE
9124     InitField(newx, newy, FALSE);
9125 #endif
9126   }
9127   else if (element == EL_DC_MAGIC_WALL_FILLING)
9128   {
9129     element = Feld[newx][newy] = get_next_element(element);
9130     if (!game.magic_wall_active)
9131       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9132     Store[newx][newy] = Store[x][y];
9133   }
9134   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9135   {
9136     Feld[x][y] = get_next_element(element);
9137     if (!game.magic_wall_active)
9138       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9139     element = Feld[newx][newy] = Store[x][y];
9140
9141 #if USE_NEW_CUSTOM_VALUE
9142     InitField(newx, newy, FALSE);
9143 #endif
9144   }
9145   else if (element == EL_AMOEBA_DROPPING)
9146   {
9147     Feld[x][y] = get_next_element(element);
9148     element = Feld[newx][newy] = Store[x][y];
9149   }
9150   else if (element == EL_SOKOBAN_OBJECT)
9151   {
9152     if (Back[x][y])
9153       Feld[x][y] = Back[x][y];
9154
9155     if (Back[newx][newy])
9156       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9157
9158     Back[x][y] = Back[newx][newy] = 0;
9159   }
9160
9161   Store[x][y] = EL_EMPTY;
9162   MovPos[x][y] = 0;
9163   MovDir[x][y] = 0;
9164   MovDelay[x][y] = 0;
9165
9166   MovDelay[newx][newy] = 0;
9167
9168   if (CAN_CHANGE_OR_HAS_ACTION(element))
9169   {
9170     /* copy element change control values to new field */
9171     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9172     ChangePage[newx][newy]  = ChangePage[x][y];
9173     ChangeCount[newx][newy] = ChangeCount[x][y];
9174     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9175   }
9176
9177 #if USE_NEW_CUSTOM_VALUE
9178   CustomValue[newx][newy] = CustomValue[x][y];
9179 #endif
9180
9181   ChangeDelay[x][y] = 0;
9182   ChangePage[x][y] = -1;
9183   ChangeCount[x][y] = 0;
9184   ChangeEvent[x][y] = -1;
9185
9186 #if USE_NEW_CUSTOM_VALUE
9187   CustomValue[x][y] = 0;
9188 #endif
9189
9190   /* copy animation control values to new field */
9191   GfxFrame[newx][newy]  = GfxFrame[x][y];
9192   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9193   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9194   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9195
9196   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9197
9198   /* some elements can leave other elements behind after moving */
9199 #if 1
9200   if (ei->move_leave_element != EL_EMPTY &&
9201       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9202       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9203 #else
9204   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9205       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9206       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9207 #endif
9208   {
9209     int move_leave_element = ei->move_leave_element;
9210
9211 #if 1
9212 #if 1
9213     /* this makes it possible to leave the removed element again */
9214     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9215       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9216 #else
9217     /* this makes it possible to leave the removed element again */
9218     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9219       move_leave_element = stored;
9220 #endif
9221 #else
9222     /* this makes it possible to leave the removed element again */
9223     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9224         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9225       move_leave_element = stored;
9226 #endif
9227
9228     Feld[x][y] = move_leave_element;
9229
9230     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9231       MovDir[x][y] = direction;
9232
9233     InitField(x, y, FALSE);
9234
9235     if (GFX_CRUMBLED(Feld[x][y]))
9236       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9237
9238     if (ELEM_IS_PLAYER(move_leave_element))
9239       RelocatePlayer(x, y, move_leave_element);
9240   }
9241
9242   /* do this after checking for left-behind element */
9243   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9244
9245   if (!CAN_MOVE(element) ||
9246       (CAN_FALL(element) && direction == MV_DOWN &&
9247        (element == EL_SPRING ||
9248         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9249         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9250     GfxDir[x][y] = MovDir[newx][newy] = 0;
9251
9252   TEST_DrawLevelField(x, y);
9253   TEST_DrawLevelField(newx, newy);
9254
9255   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9256
9257   /* prevent pushed element from moving on in pushed direction */
9258   if (pushed_by_player && CAN_MOVE(element) &&
9259       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9260       !(element_info[element].move_pattern & direction))
9261     TurnRound(newx, newy);
9262
9263   /* prevent elements on conveyor belt from moving on in last direction */
9264   if (pushed_by_conveyor && CAN_FALL(element) &&
9265       direction & MV_HORIZONTAL)
9266     MovDir[newx][newy] = 0;
9267
9268   if (!pushed_by_player)
9269   {
9270     int nextx = newx + dx, nexty = newy + dy;
9271     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9272
9273     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9274
9275     if (CAN_FALL(element) && direction == MV_DOWN)
9276       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9277
9278     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9279       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9280
9281 #if USE_FIX_IMPACT_COLLISION
9282     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9283       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9284 #endif
9285   }
9286
9287   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9288   {
9289     TestIfBadThingTouchesPlayer(newx, newy);
9290     TestIfBadThingTouchesFriend(newx, newy);
9291
9292     if (!IS_CUSTOM_ELEMENT(element))
9293       TestIfBadThingTouchesOtherBadThing(newx, newy);
9294   }
9295   else if (element == EL_PENGUIN)
9296     TestIfFriendTouchesBadThing(newx, newy);
9297
9298   if (DONT_GET_HIT_BY(element))
9299   {
9300     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9301   }
9302
9303   /* give the player one last chance (one more frame) to move away */
9304   if (CAN_FALL(element) && direction == MV_DOWN &&
9305       (last_line || (!IS_FREE(x, newy + 1) &&
9306                      (!IS_PLAYER(x, newy + 1) ||
9307                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9308     Impact(x, newy);
9309
9310   if (pushed_by_player && !game.use_change_when_pushing_bug)
9311   {
9312     int push_side = MV_DIR_OPPOSITE(direction);
9313     struct PlayerInfo *player = PLAYERINFO(x, y);
9314
9315     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9316                                player->index_bit, push_side);
9317     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9318                                         player->index_bit, push_side);
9319   }
9320
9321   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9322     MovDelay[newx][newy] = 1;
9323
9324   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9325
9326   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9327
9328 #if 0
9329   if (ChangePage[newx][newy] != -1)             /* delayed change */
9330   {
9331     int page = ChangePage[newx][newy];
9332     struct ElementChangeInfo *change = &ei->change_page[page];
9333
9334     ChangePage[newx][newy] = -1;
9335
9336     if (change->can_change)
9337     {
9338       if (ChangeElement(newx, newy, element, page))
9339       {
9340         if (change->post_change_function)
9341           change->post_change_function(newx, newy);
9342       }
9343     }
9344
9345     if (change->has_action)
9346       ExecuteCustomElementAction(newx, newy, element, page);
9347   }
9348 #endif
9349
9350   TestIfElementHitsCustomElement(newx, newy, direction);
9351   TestIfPlayerTouchesCustomElement(newx, newy);
9352   TestIfElementTouchesCustomElement(newx, newy);
9353
9354   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9355       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9356     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9357                              MV_DIR_OPPOSITE(direction));
9358 }
9359
9360 int AmoebeNachbarNr(int ax, int ay)
9361 {
9362   int i;
9363   int element = Feld[ax][ay];
9364   int group_nr = 0;
9365   static int xy[4][2] =
9366   {
9367     { 0, -1 },
9368     { -1, 0 },
9369     { +1, 0 },
9370     { 0, +1 }
9371   };
9372
9373   for (i = 0; i < NUM_DIRECTIONS; i++)
9374   {
9375     int x = ax + xy[i][0];
9376     int y = ay + xy[i][1];
9377
9378     if (!IN_LEV_FIELD(x, y))
9379       continue;
9380
9381     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9382       group_nr = AmoebaNr[x][y];
9383   }
9384
9385   return group_nr;
9386 }
9387
9388 void AmoebenVereinigen(int ax, int ay)
9389 {
9390   int i, x, y, xx, yy;
9391   int new_group_nr = AmoebaNr[ax][ay];
9392   static int xy[4][2] =
9393   {
9394     { 0, -1 },
9395     { -1, 0 },
9396     { +1, 0 },
9397     { 0, +1 }
9398   };
9399
9400   if (new_group_nr == 0)
9401     return;
9402
9403   for (i = 0; i < NUM_DIRECTIONS; i++)
9404   {
9405     x = ax + xy[i][0];
9406     y = ay + xy[i][1];
9407
9408     if (!IN_LEV_FIELD(x, y))
9409       continue;
9410
9411     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9412          Feld[x][y] == EL_BD_AMOEBA ||
9413          Feld[x][y] == EL_AMOEBA_DEAD) &&
9414         AmoebaNr[x][y] != new_group_nr)
9415     {
9416       int old_group_nr = AmoebaNr[x][y];
9417
9418       if (old_group_nr == 0)
9419         return;
9420
9421       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9422       AmoebaCnt[old_group_nr] = 0;
9423       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9424       AmoebaCnt2[old_group_nr] = 0;
9425
9426       SCAN_PLAYFIELD(xx, yy)
9427       {
9428         if (AmoebaNr[xx][yy] == old_group_nr)
9429           AmoebaNr[xx][yy] = new_group_nr;
9430       }
9431     }
9432   }
9433 }
9434
9435 void AmoebeUmwandeln(int ax, int ay)
9436 {
9437   int i, x, y;
9438
9439   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9440   {
9441     int group_nr = AmoebaNr[ax][ay];
9442
9443 #ifdef DEBUG
9444     if (group_nr == 0)
9445     {
9446       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9447       printf("AmoebeUmwandeln(): This should never happen!\n");
9448       return;
9449     }
9450 #endif
9451
9452     SCAN_PLAYFIELD(x, y)
9453     {
9454       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9455       {
9456         AmoebaNr[x][y] = 0;
9457         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9458       }
9459     }
9460
9461     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9462                             SND_AMOEBA_TURNING_TO_GEM :
9463                             SND_AMOEBA_TURNING_TO_ROCK));
9464     Bang(ax, ay);
9465   }
9466   else
9467   {
9468     static int xy[4][2] =
9469     {
9470       { 0, -1 },
9471       { -1, 0 },
9472       { +1, 0 },
9473       { 0, +1 }
9474     };
9475
9476     for (i = 0; i < NUM_DIRECTIONS; i++)
9477     {
9478       x = ax + xy[i][0];
9479       y = ay + xy[i][1];
9480
9481       if (!IN_LEV_FIELD(x, y))
9482         continue;
9483
9484       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9485       {
9486         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9487                               SND_AMOEBA_TURNING_TO_GEM :
9488                               SND_AMOEBA_TURNING_TO_ROCK));
9489         Bang(x, y);
9490       }
9491     }
9492   }
9493 }
9494
9495 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9496 {
9497   int x, y;
9498   int group_nr = AmoebaNr[ax][ay];
9499   boolean done = FALSE;
9500
9501 #ifdef DEBUG
9502   if (group_nr == 0)
9503   {
9504     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9505     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9506     return;
9507   }
9508 #endif
9509
9510   SCAN_PLAYFIELD(x, y)
9511   {
9512     if (AmoebaNr[x][y] == group_nr &&
9513         (Feld[x][y] == EL_AMOEBA_DEAD ||
9514          Feld[x][y] == EL_BD_AMOEBA ||
9515          Feld[x][y] == EL_AMOEBA_GROWING))
9516     {
9517       AmoebaNr[x][y] = 0;
9518       Feld[x][y] = new_element;
9519       InitField(x, y, FALSE);
9520       TEST_DrawLevelField(x, y);
9521       done = TRUE;
9522     }
9523   }
9524
9525   if (done)
9526     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9527                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9528                             SND_BD_AMOEBA_TURNING_TO_GEM));
9529 }
9530
9531 void AmoebeWaechst(int x, int y)
9532 {
9533   static unsigned long sound_delay = 0;
9534   static unsigned long sound_delay_value = 0;
9535
9536   if (!MovDelay[x][y])          /* start new growing cycle */
9537   {
9538     MovDelay[x][y] = 7;
9539
9540     if (DelayReached(&sound_delay, sound_delay_value))
9541     {
9542       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9543       sound_delay_value = 30;
9544     }
9545   }
9546
9547   if (MovDelay[x][y])           /* wait some time before growing bigger */
9548   {
9549     MovDelay[x][y]--;
9550     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9551     {
9552       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9553                                            6 - MovDelay[x][y]);
9554
9555       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9556     }
9557
9558     if (!MovDelay[x][y])
9559     {
9560       Feld[x][y] = Store[x][y];
9561       Store[x][y] = 0;
9562       TEST_DrawLevelField(x, y);
9563     }
9564   }
9565 }
9566
9567 void AmoebaDisappearing(int x, int y)
9568 {
9569   static unsigned long sound_delay = 0;
9570   static unsigned long sound_delay_value = 0;
9571
9572   if (!MovDelay[x][y])          /* start new shrinking cycle */
9573   {
9574     MovDelay[x][y] = 7;
9575
9576     if (DelayReached(&sound_delay, sound_delay_value))
9577       sound_delay_value = 30;
9578   }
9579
9580   if (MovDelay[x][y])           /* wait some time before shrinking */
9581   {
9582     MovDelay[x][y]--;
9583     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9584     {
9585       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9586                                            6 - MovDelay[x][y]);
9587
9588       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9589     }
9590
9591     if (!MovDelay[x][y])
9592     {
9593       Feld[x][y] = EL_EMPTY;
9594       TEST_DrawLevelField(x, y);
9595
9596       /* don't let mole enter this field in this cycle;
9597          (give priority to objects falling to this field from above) */
9598       Stop[x][y] = TRUE;
9599     }
9600   }
9601 }
9602
9603 void AmoebeAbleger(int ax, int ay)
9604 {
9605   int i;
9606   int element = Feld[ax][ay];
9607   int graphic = el2img(element);
9608   int newax = ax, neway = ay;
9609   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9610   static int xy[4][2] =
9611   {
9612     { 0, -1 },
9613     { -1, 0 },
9614     { +1, 0 },
9615     { 0, +1 }
9616   };
9617
9618   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9619   {
9620     Feld[ax][ay] = EL_AMOEBA_DEAD;
9621     TEST_DrawLevelField(ax, ay);
9622     return;
9623   }
9624
9625   if (IS_ANIMATED(graphic))
9626     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9627
9628   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9629     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9630
9631   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9632   {
9633     MovDelay[ax][ay]--;
9634     if (MovDelay[ax][ay])
9635       return;
9636   }
9637
9638   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9639   {
9640     int start = RND(4);
9641     int x = ax + xy[start][0];
9642     int y = ay + xy[start][1];
9643
9644     if (!IN_LEV_FIELD(x, y))
9645       return;
9646
9647     if (IS_FREE(x, y) ||
9648         CAN_GROW_INTO(Feld[x][y]) ||
9649         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9650         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9651     {
9652       newax = x;
9653       neway = y;
9654     }
9655
9656     if (newax == ax && neway == ay)
9657       return;
9658   }
9659   else                          /* normal or "filled" (BD style) amoeba */
9660   {
9661     int start = RND(4);
9662     boolean waiting_for_player = FALSE;
9663
9664     for (i = 0; i < NUM_DIRECTIONS; i++)
9665     {
9666       int j = (start + i) % 4;
9667       int x = ax + xy[j][0];
9668       int y = ay + xy[j][1];
9669
9670       if (!IN_LEV_FIELD(x, y))
9671         continue;
9672
9673       if (IS_FREE(x, y) ||
9674           CAN_GROW_INTO(Feld[x][y]) ||
9675           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9676           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9677       {
9678         newax = x;
9679         neway = y;
9680         break;
9681       }
9682       else if (IS_PLAYER(x, y))
9683         waiting_for_player = TRUE;
9684     }
9685
9686     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9687     {
9688       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9689       {
9690         Feld[ax][ay] = EL_AMOEBA_DEAD;
9691         TEST_DrawLevelField(ax, ay);
9692         AmoebaCnt[AmoebaNr[ax][ay]]--;
9693
9694         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9695         {
9696           if (element == EL_AMOEBA_FULL)
9697             AmoebeUmwandeln(ax, ay);
9698           else if (element == EL_BD_AMOEBA)
9699             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9700         }
9701       }
9702       return;
9703     }
9704     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9705     {
9706       /* amoeba gets larger by growing in some direction */
9707
9708       int new_group_nr = AmoebaNr[ax][ay];
9709
9710 #ifdef DEBUG
9711   if (new_group_nr == 0)
9712   {
9713     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9714     printf("AmoebeAbleger(): This should never happen!\n");
9715     return;
9716   }
9717 #endif
9718
9719       AmoebaNr[newax][neway] = new_group_nr;
9720       AmoebaCnt[new_group_nr]++;
9721       AmoebaCnt2[new_group_nr]++;
9722
9723       /* if amoeba touches other amoeba(s) after growing, unify them */
9724       AmoebenVereinigen(newax, neway);
9725
9726       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9727       {
9728         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9729         return;
9730       }
9731     }
9732   }
9733
9734   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9735       (neway == lev_fieldy - 1 && newax != ax))
9736   {
9737     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9738     Store[newax][neway] = element;
9739   }
9740   else if (neway == ay || element == EL_EMC_DRIPPER)
9741   {
9742     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9743
9744     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9745   }
9746   else
9747   {
9748     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9749     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9750     Store[ax][ay] = EL_AMOEBA_DROP;
9751     ContinueMoving(ax, ay);
9752     return;
9753   }
9754
9755   TEST_DrawLevelField(newax, neway);
9756 }
9757
9758 void Life(int ax, int ay)
9759 {
9760   int x1, y1, x2, y2;
9761   int life_time = 40;
9762   int element = Feld[ax][ay];
9763   int graphic = el2img(element);
9764   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9765                          level.biomaze);
9766   boolean changed = FALSE;
9767
9768   if (IS_ANIMATED(graphic))
9769     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9770
9771   if (Stop[ax][ay])
9772     return;
9773
9774   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9775     MovDelay[ax][ay] = life_time;
9776
9777   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9778   {
9779     MovDelay[ax][ay]--;
9780     if (MovDelay[ax][ay])
9781       return;
9782   }
9783
9784   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9785   {
9786     int xx = ax+x1, yy = ay+y1;
9787     int nachbarn = 0;
9788
9789     if (!IN_LEV_FIELD(xx, yy))
9790       continue;
9791
9792     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9793     {
9794       int x = xx+x2, y = yy+y2;
9795
9796       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9797         continue;
9798
9799       if (((Feld[x][y] == element ||
9800             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9801            !Stop[x][y]) ||
9802           (IS_FREE(x, y) && Stop[x][y]))
9803         nachbarn++;
9804     }
9805
9806     if (xx == ax && yy == ay)           /* field in the middle */
9807     {
9808       if (nachbarn < life_parameter[0] ||
9809           nachbarn > life_parameter[1])
9810       {
9811         Feld[xx][yy] = EL_EMPTY;
9812         if (!Stop[xx][yy])
9813           TEST_DrawLevelField(xx, yy);
9814         Stop[xx][yy] = TRUE;
9815         changed = TRUE;
9816       }
9817     }
9818     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9819     {                                   /* free border field */
9820       if (nachbarn >= life_parameter[2] &&
9821           nachbarn <= life_parameter[3])
9822       {
9823         Feld[xx][yy] = element;
9824         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9825         if (!Stop[xx][yy])
9826           TEST_DrawLevelField(xx, yy);
9827         Stop[xx][yy] = TRUE;
9828         changed = TRUE;
9829       }
9830     }
9831   }
9832
9833   if (changed)
9834     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9835                    SND_GAME_OF_LIFE_GROWING);
9836 }
9837
9838 static void InitRobotWheel(int x, int y)
9839 {
9840   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9841 }
9842
9843 static void RunRobotWheel(int x, int y)
9844 {
9845   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9846 }
9847
9848 static void StopRobotWheel(int x, int y)
9849 {
9850   if (ZX == x && ZY == y)
9851   {
9852     ZX = ZY = -1;
9853
9854     game.robot_wheel_active = FALSE;
9855   }
9856 }
9857
9858 static void InitTimegateWheel(int x, int y)
9859 {
9860   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9861 }
9862
9863 static void RunTimegateWheel(int x, int y)
9864 {
9865   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9866 }
9867
9868 static void InitMagicBallDelay(int x, int y)
9869 {
9870 #if 1
9871   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9872 #else
9873   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9874 #endif
9875 }
9876
9877 static void ActivateMagicBall(int bx, int by)
9878 {
9879   int x, y;
9880
9881   if (level.ball_random)
9882   {
9883     int pos_border = RND(8);    /* select one of the eight border elements */
9884     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9885     int xx = pos_content % 3;
9886     int yy = pos_content / 3;
9887
9888     x = bx - 1 + xx;
9889     y = by - 1 + yy;
9890
9891     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9892       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9893   }
9894   else
9895   {
9896     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9897     {
9898       int xx = x - bx + 1;
9899       int yy = y - by + 1;
9900
9901       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9902         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9903     }
9904   }
9905
9906   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9907 }
9908
9909 void CheckExit(int x, int y)
9910 {
9911   if (local_player->gems_still_needed > 0 ||
9912       local_player->sokobanfields_still_needed > 0 ||
9913       local_player->lights_still_needed > 0)
9914   {
9915     int element = Feld[x][y];
9916     int graphic = el2img(element);
9917
9918     if (IS_ANIMATED(graphic))
9919       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9920
9921     return;
9922   }
9923
9924   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9925     return;
9926
9927   Feld[x][y] = EL_EXIT_OPENING;
9928
9929   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9930 }
9931
9932 void CheckExitEM(int x, int y)
9933 {
9934   if (local_player->gems_still_needed > 0 ||
9935       local_player->sokobanfields_still_needed > 0 ||
9936       local_player->lights_still_needed > 0)
9937   {
9938     int element = Feld[x][y];
9939     int graphic = el2img(element);
9940
9941     if (IS_ANIMATED(graphic))
9942       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9943
9944     return;
9945   }
9946
9947   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9948     return;
9949
9950   Feld[x][y] = EL_EM_EXIT_OPENING;
9951
9952   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9953 }
9954
9955 void CheckExitSteel(int x, int y)
9956 {
9957   if (local_player->gems_still_needed > 0 ||
9958       local_player->sokobanfields_still_needed > 0 ||
9959       local_player->lights_still_needed > 0)
9960   {
9961     int element = Feld[x][y];
9962     int graphic = el2img(element);
9963
9964     if (IS_ANIMATED(graphic))
9965       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9966
9967     return;
9968   }
9969
9970   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9971     return;
9972
9973   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9974
9975   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9976 }
9977
9978 void CheckExitSteelEM(int x, int y)
9979 {
9980   if (local_player->gems_still_needed > 0 ||
9981       local_player->sokobanfields_still_needed > 0 ||
9982       local_player->lights_still_needed > 0)
9983   {
9984     int element = Feld[x][y];
9985     int graphic = el2img(element);
9986
9987     if (IS_ANIMATED(graphic))
9988       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9989
9990     return;
9991   }
9992
9993   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9994     return;
9995
9996   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9997
9998   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9999 }
10000
10001 void CheckExitSP(int x, int y)
10002 {
10003   if (local_player->gems_still_needed > 0)
10004   {
10005     int element = Feld[x][y];
10006     int graphic = el2img(element);
10007
10008     if (IS_ANIMATED(graphic))
10009       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10010
10011     return;
10012   }
10013
10014   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10015     return;
10016
10017   Feld[x][y] = EL_SP_EXIT_OPENING;
10018
10019   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10020 }
10021
10022 static void CloseAllOpenTimegates()
10023 {
10024   int x, y;
10025
10026   SCAN_PLAYFIELD(x, y)
10027   {
10028     int element = Feld[x][y];
10029
10030     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10031     {
10032       Feld[x][y] = EL_TIMEGATE_CLOSING;
10033
10034       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10035     }
10036   }
10037 }
10038
10039 void DrawTwinkleOnField(int x, int y)
10040 {
10041   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10042     return;
10043
10044   if (Feld[x][y] == EL_BD_DIAMOND)
10045     return;
10046
10047   if (MovDelay[x][y] == 0)      /* next animation frame */
10048     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10049
10050   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10051   {
10052     MovDelay[x][y]--;
10053
10054     DrawLevelElementAnimation(x, y, Feld[x][y]);
10055
10056     if (MovDelay[x][y] != 0)
10057     {
10058       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10059                                            10 - MovDelay[x][y]);
10060
10061       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10062     }
10063   }
10064 }
10065
10066 void MauerWaechst(int x, int y)
10067 {
10068   int delay = 6;
10069
10070   if (!MovDelay[x][y])          /* next animation frame */
10071     MovDelay[x][y] = 3 * delay;
10072
10073   if (MovDelay[x][y])           /* wait some time before next frame */
10074   {
10075     MovDelay[x][y]--;
10076
10077     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10078     {
10079       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10080       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10081
10082       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10083     }
10084
10085     if (!MovDelay[x][y])
10086     {
10087       if (MovDir[x][y] == MV_LEFT)
10088       {
10089         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10090           TEST_DrawLevelField(x - 1, y);
10091       }
10092       else if (MovDir[x][y] == MV_RIGHT)
10093       {
10094         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10095           TEST_DrawLevelField(x + 1, y);
10096       }
10097       else if (MovDir[x][y] == MV_UP)
10098       {
10099         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10100           TEST_DrawLevelField(x, y - 1);
10101       }
10102       else
10103       {
10104         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10105           TEST_DrawLevelField(x, y + 1);
10106       }
10107
10108       Feld[x][y] = Store[x][y];
10109       Store[x][y] = 0;
10110       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10111       TEST_DrawLevelField(x, y);
10112     }
10113   }
10114 }
10115
10116 void MauerAbleger(int ax, int ay)
10117 {
10118   int element = Feld[ax][ay];
10119   int graphic = el2img(element);
10120   boolean oben_frei = FALSE, unten_frei = FALSE;
10121   boolean links_frei = FALSE, rechts_frei = FALSE;
10122   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10123   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10124   boolean new_wall = FALSE;
10125
10126   if (IS_ANIMATED(graphic))
10127     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10128
10129   if (!MovDelay[ax][ay])        /* start building new wall */
10130     MovDelay[ax][ay] = 6;
10131
10132   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10133   {
10134     MovDelay[ax][ay]--;
10135     if (MovDelay[ax][ay])
10136       return;
10137   }
10138
10139   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10140     oben_frei = TRUE;
10141   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10142     unten_frei = TRUE;
10143   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10144     links_frei = TRUE;
10145   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10146     rechts_frei = TRUE;
10147
10148   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10149       element == EL_EXPANDABLE_WALL_ANY)
10150   {
10151     if (oben_frei)
10152     {
10153       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10154       Store[ax][ay-1] = element;
10155       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10156       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10157         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10158                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10159       new_wall = TRUE;
10160     }
10161     if (unten_frei)
10162     {
10163       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10164       Store[ax][ay+1] = element;
10165       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10166       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10167         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10168                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10169       new_wall = TRUE;
10170     }
10171   }
10172
10173   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10174       element == EL_EXPANDABLE_WALL_ANY ||
10175       element == EL_EXPANDABLE_WALL ||
10176       element == EL_BD_EXPANDABLE_WALL)
10177   {
10178     if (links_frei)
10179     {
10180       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10181       Store[ax-1][ay] = element;
10182       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10183       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10184         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10185                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10186       new_wall = TRUE;
10187     }
10188
10189     if (rechts_frei)
10190     {
10191       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10192       Store[ax+1][ay] = element;
10193       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10194       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10195         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10196                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10197       new_wall = TRUE;
10198     }
10199   }
10200
10201   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10202     TEST_DrawLevelField(ax, ay);
10203
10204   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10205     oben_massiv = TRUE;
10206   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10207     unten_massiv = TRUE;
10208   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10209     links_massiv = TRUE;
10210   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10211     rechts_massiv = TRUE;
10212
10213   if (((oben_massiv && unten_massiv) ||
10214        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10215        element == EL_EXPANDABLE_WALL) &&
10216       ((links_massiv && rechts_massiv) ||
10217        element == EL_EXPANDABLE_WALL_VERTICAL))
10218     Feld[ax][ay] = EL_WALL;
10219
10220   if (new_wall)
10221     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10222 }
10223
10224 void MauerAblegerStahl(int ax, int ay)
10225 {
10226   int element = Feld[ax][ay];
10227   int graphic = el2img(element);
10228   boolean oben_frei = FALSE, unten_frei = FALSE;
10229   boolean links_frei = FALSE, rechts_frei = FALSE;
10230   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10231   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10232   boolean new_wall = FALSE;
10233
10234   if (IS_ANIMATED(graphic))
10235     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10236
10237   if (!MovDelay[ax][ay])        /* start building new wall */
10238     MovDelay[ax][ay] = 6;
10239
10240   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10241   {
10242     MovDelay[ax][ay]--;
10243     if (MovDelay[ax][ay])
10244       return;
10245   }
10246
10247   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10248     oben_frei = TRUE;
10249   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10250     unten_frei = TRUE;
10251   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10252     links_frei = TRUE;
10253   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10254     rechts_frei = TRUE;
10255
10256   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10257       element == EL_EXPANDABLE_STEELWALL_ANY)
10258   {
10259     if (oben_frei)
10260     {
10261       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10262       Store[ax][ay-1] = element;
10263       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10264       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10265         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10266                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10267       new_wall = TRUE;
10268     }
10269     if (unten_frei)
10270     {
10271       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10272       Store[ax][ay+1] = element;
10273       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10274       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10275         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10276                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10277       new_wall = TRUE;
10278     }
10279   }
10280
10281   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10282       element == EL_EXPANDABLE_STEELWALL_ANY)
10283   {
10284     if (links_frei)
10285     {
10286       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10287       Store[ax-1][ay] = element;
10288       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10289       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10290         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10291                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10292       new_wall = TRUE;
10293     }
10294
10295     if (rechts_frei)
10296     {
10297       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10298       Store[ax+1][ay] = element;
10299       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10300       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10301         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10302                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10303       new_wall = TRUE;
10304     }
10305   }
10306
10307   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10308     oben_massiv = TRUE;
10309   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10310     unten_massiv = TRUE;
10311   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10312     links_massiv = TRUE;
10313   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10314     rechts_massiv = TRUE;
10315
10316   if (((oben_massiv && unten_massiv) ||
10317        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10318       ((links_massiv && rechts_massiv) ||
10319        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10320     Feld[ax][ay] = EL_STEELWALL;
10321
10322   if (new_wall)
10323     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10324 }
10325
10326 void CheckForDragon(int x, int y)
10327 {
10328   int i, j;
10329   boolean dragon_found = FALSE;
10330   static int xy[4][2] =
10331   {
10332     { 0, -1 },
10333     { -1, 0 },
10334     { +1, 0 },
10335     { 0, +1 }
10336   };
10337
10338   for (i = 0; i < NUM_DIRECTIONS; i++)
10339   {
10340     for (j = 0; j < 4; j++)
10341     {
10342       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10343
10344       if (IN_LEV_FIELD(xx, yy) &&
10345           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10346       {
10347         if (Feld[xx][yy] == EL_DRAGON)
10348           dragon_found = TRUE;
10349       }
10350       else
10351         break;
10352     }
10353   }
10354
10355   if (!dragon_found)
10356   {
10357     for (i = 0; i < NUM_DIRECTIONS; i++)
10358     {
10359       for (j = 0; j < 3; j++)
10360       {
10361         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10362   
10363         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10364         {
10365           Feld[xx][yy] = EL_EMPTY;
10366           TEST_DrawLevelField(xx, yy);
10367         }
10368         else
10369           break;
10370       }
10371     }
10372   }
10373 }
10374
10375 static void InitBuggyBase(int x, int y)
10376 {
10377   int element = Feld[x][y];
10378   int activating_delay = FRAMES_PER_SECOND / 4;
10379
10380   ChangeDelay[x][y] =
10381     (element == EL_SP_BUGGY_BASE ?
10382      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10383      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10384      activating_delay :
10385      element == EL_SP_BUGGY_BASE_ACTIVE ?
10386      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10387 }
10388
10389 static void WarnBuggyBase(int x, int y)
10390 {
10391   int i;
10392   static int xy[4][2] =
10393   {
10394     { 0, -1 },
10395     { -1, 0 },
10396     { +1, 0 },
10397     { 0, +1 }
10398   };
10399
10400   for (i = 0; i < NUM_DIRECTIONS; i++)
10401   {
10402     int xx = x + xy[i][0];
10403     int yy = y + xy[i][1];
10404
10405     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10406     {
10407       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10408
10409       break;
10410     }
10411   }
10412 }
10413
10414 static void InitTrap(int x, int y)
10415 {
10416   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10417 }
10418
10419 static void ActivateTrap(int x, int y)
10420 {
10421   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10422 }
10423
10424 static void ChangeActiveTrap(int x, int y)
10425 {
10426   int graphic = IMG_TRAP_ACTIVE;
10427
10428   /* if new animation frame was drawn, correct crumbled sand border */
10429   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10430     TEST_DrawLevelFieldCrumbled(x, y);
10431 }
10432
10433 static int getSpecialActionElement(int element, int number, int base_element)
10434 {
10435   return (element != EL_EMPTY ? element :
10436           number != -1 ? base_element + number - 1 :
10437           EL_EMPTY);
10438 }
10439
10440 static int getModifiedActionNumber(int value_old, int operator, int operand,
10441                                    int value_min, int value_max)
10442 {
10443   int value_new = (operator == CA_MODE_SET      ? operand :
10444                    operator == CA_MODE_ADD      ? value_old + operand :
10445                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10446                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10447                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10448                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10449                    value_old);
10450
10451   return (value_new < value_min ? value_min :
10452           value_new > value_max ? value_max :
10453           value_new);
10454 }
10455
10456 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10457 {
10458   struct ElementInfo *ei = &element_info[element];
10459   struct ElementChangeInfo *change = &ei->change_page[page];
10460   int target_element = change->target_element;
10461   int action_type = change->action_type;
10462   int action_mode = change->action_mode;
10463   int action_arg = change->action_arg;
10464   int action_element = change->action_element;
10465   int i;
10466
10467   if (!change->has_action)
10468     return;
10469
10470   /* ---------- determine action paramater values -------------------------- */
10471
10472   int level_time_value =
10473     (level.time > 0 ? TimeLeft :
10474      TimePlayed);
10475
10476   int action_arg_element_raw =
10477     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10478      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10479      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10480      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10481      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10482      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10483      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10484      EL_EMPTY);
10485   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10486
10487 #if 0
10488   if (action_arg_element_raw == EL_GROUP_START)
10489     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10490 #endif
10491
10492   int action_arg_direction =
10493     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10494      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10495      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10496      change->actual_trigger_side :
10497      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10498      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10499      MV_NONE);
10500
10501   int action_arg_number_min =
10502     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10503      CA_ARG_MIN);
10504
10505   int action_arg_number_max =
10506     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10507      action_type == CA_SET_LEVEL_GEMS ? 999 :
10508      action_type == CA_SET_LEVEL_TIME ? 9999 :
10509      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10510      action_type == CA_SET_CE_VALUE ? 9999 :
10511      action_type == CA_SET_CE_SCORE ? 9999 :
10512      CA_ARG_MAX);
10513
10514   int action_arg_number_reset =
10515     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10516      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10517      action_type == CA_SET_LEVEL_TIME ? level.time :
10518      action_type == CA_SET_LEVEL_SCORE ? 0 :
10519 #if USE_NEW_CUSTOM_VALUE
10520      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10521 #else
10522      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10523 #endif
10524      action_type == CA_SET_CE_SCORE ? 0 :
10525      0);
10526
10527   int action_arg_number =
10528     (action_arg <= CA_ARG_MAX ? action_arg :
10529      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10530      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10531      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10532      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10533      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10534      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10535 #if USE_NEW_CUSTOM_VALUE
10536      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10537 #else
10538      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10539 #endif
10540      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10541      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10542      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10543      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10544      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10545      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10546      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10547      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10548      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10549      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10550      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10551      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10552      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10553      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10554      -1);
10555
10556   int action_arg_number_old =
10557     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10558      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10559      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10560      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10561      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10562      0);
10563
10564   int action_arg_number_new =
10565     getModifiedActionNumber(action_arg_number_old,
10566                             action_mode, action_arg_number,
10567                             action_arg_number_min, action_arg_number_max);
10568
10569 #if 1
10570   int trigger_player_bits =
10571     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10572      change->actual_trigger_player_bits : change->trigger_player);
10573 #else
10574   int trigger_player_bits =
10575     (change->actual_trigger_player >= EL_PLAYER_1 &&
10576      change->actual_trigger_player <= EL_PLAYER_4 ?
10577      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10578      PLAYER_BITS_ANY);
10579 #endif
10580
10581   int action_arg_player_bits =
10582     (action_arg >= CA_ARG_PLAYER_1 &&
10583      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10584      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10585      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10586      PLAYER_BITS_ANY);
10587
10588   /* ---------- execute action  -------------------------------------------- */
10589
10590   switch (action_type)
10591   {
10592     case CA_NO_ACTION:
10593     {
10594       return;
10595     }
10596
10597     /* ---------- level actions  ------------------------------------------- */
10598
10599     case CA_RESTART_LEVEL:
10600     {
10601       game.restart_level = TRUE;
10602
10603       break;
10604     }
10605
10606     case CA_SHOW_ENVELOPE:
10607     {
10608       int element = getSpecialActionElement(action_arg_element,
10609                                             action_arg_number, EL_ENVELOPE_1);
10610
10611       if (IS_ENVELOPE(element))
10612         local_player->show_envelope = element;
10613
10614       break;
10615     }
10616
10617     case CA_SET_LEVEL_TIME:
10618     {
10619       if (level.time > 0)       /* only modify limited time value */
10620       {
10621         TimeLeft = action_arg_number_new;
10622
10623 #if 1
10624         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10625
10626         DisplayGameControlValues();
10627 #else
10628         DrawGameValue_Time(TimeLeft);
10629 #endif
10630
10631         if (!TimeLeft && setup.time_limit)
10632           for (i = 0; i < MAX_PLAYERS; i++)
10633             KillPlayer(&stored_player[i]);
10634       }
10635
10636       break;
10637     }
10638
10639     case CA_SET_LEVEL_SCORE:
10640     {
10641       local_player->score = action_arg_number_new;
10642
10643 #if 1
10644       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10645
10646       DisplayGameControlValues();
10647 #else
10648       DrawGameValue_Score(local_player->score);
10649 #endif
10650
10651       break;
10652     }
10653
10654     case CA_SET_LEVEL_GEMS:
10655     {
10656       local_player->gems_still_needed = action_arg_number_new;
10657
10658 #if 1
10659       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10660
10661       DisplayGameControlValues();
10662 #else
10663       DrawGameValue_Emeralds(local_player->gems_still_needed);
10664 #endif
10665
10666       break;
10667     }
10668
10669 #if !USE_PLAYER_GRAVITY
10670     case CA_SET_LEVEL_GRAVITY:
10671     {
10672       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10673                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10674                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10675                       game.gravity);
10676       break;
10677     }
10678 #endif
10679
10680     case CA_SET_LEVEL_WIND:
10681     {
10682       game.wind_direction = action_arg_direction;
10683
10684       break;
10685     }
10686
10687     case CA_SET_LEVEL_RANDOM_SEED:
10688     {
10689 #if 1
10690       /* ensure that setting a new random seed while playing is predictable */
10691       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10692 #else
10693       InitRND(action_arg_number_new);
10694 #endif
10695
10696 #if 0
10697       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10698 #endif
10699
10700 #if 0
10701       {
10702         int i;
10703
10704         printf("::: ");
10705         for (i = 0; i < 9; i++)
10706           printf("%d, ", RND(2));
10707         printf("\n");
10708       }
10709 #endif
10710
10711       break;
10712     }
10713
10714     /* ---------- player actions  ------------------------------------------ */
10715
10716     case CA_MOVE_PLAYER:
10717     {
10718       /* automatically move to the next field in specified direction */
10719       for (i = 0; i < MAX_PLAYERS; i++)
10720         if (trigger_player_bits & (1 << i))
10721           stored_player[i].programmed_action = action_arg_direction;
10722
10723       break;
10724     }
10725
10726     case CA_EXIT_PLAYER:
10727     {
10728       for (i = 0; i < MAX_PLAYERS; i++)
10729         if (action_arg_player_bits & (1 << i))
10730           PlayerWins(&stored_player[i]);
10731
10732       break;
10733     }
10734
10735     case CA_KILL_PLAYER:
10736     {
10737       for (i = 0; i < MAX_PLAYERS; i++)
10738         if (action_arg_player_bits & (1 << i))
10739           KillPlayer(&stored_player[i]);
10740
10741       break;
10742     }
10743
10744     case CA_SET_PLAYER_KEYS:
10745     {
10746       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10747       int element = getSpecialActionElement(action_arg_element,
10748                                             action_arg_number, EL_KEY_1);
10749
10750       if (IS_KEY(element))
10751       {
10752         for (i = 0; i < MAX_PLAYERS; i++)
10753         {
10754           if (trigger_player_bits & (1 << i))
10755           {
10756             stored_player[i].key[KEY_NR(element)] = key_state;
10757
10758             DrawGameDoorValues();
10759           }
10760         }
10761       }
10762
10763       break;
10764     }
10765
10766     case CA_SET_PLAYER_SPEED:
10767     {
10768 #if 0
10769       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10770 #endif
10771
10772       for (i = 0; i < MAX_PLAYERS; i++)
10773       {
10774         if (trigger_player_bits & (1 << i))
10775         {
10776           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10777
10778           if (action_arg == CA_ARG_SPEED_FASTER &&
10779               stored_player[i].cannot_move)
10780           {
10781             action_arg_number = STEPSIZE_VERY_SLOW;
10782           }
10783           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10784                    action_arg == CA_ARG_SPEED_FASTER)
10785           {
10786             action_arg_number = 2;
10787             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10788                            CA_MODE_MULTIPLY);
10789           }
10790           else if (action_arg == CA_ARG_NUMBER_RESET)
10791           {
10792             action_arg_number = level.initial_player_stepsize[i];
10793           }
10794
10795           move_stepsize =
10796             getModifiedActionNumber(move_stepsize,
10797                                     action_mode,
10798                                     action_arg_number,
10799                                     action_arg_number_min,
10800                                     action_arg_number_max);
10801
10802           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10803         }
10804       }
10805
10806       break;
10807     }
10808
10809     case CA_SET_PLAYER_SHIELD:
10810     {
10811       for (i = 0; i < MAX_PLAYERS; i++)
10812       {
10813         if (trigger_player_bits & (1 << i))
10814         {
10815           if (action_arg == CA_ARG_SHIELD_OFF)
10816           {
10817             stored_player[i].shield_normal_time_left = 0;
10818             stored_player[i].shield_deadly_time_left = 0;
10819           }
10820           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10821           {
10822             stored_player[i].shield_normal_time_left = 999999;
10823           }
10824           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10825           {
10826             stored_player[i].shield_normal_time_left = 999999;
10827             stored_player[i].shield_deadly_time_left = 999999;
10828           }
10829         }
10830       }
10831
10832       break;
10833     }
10834
10835 #if USE_PLAYER_GRAVITY
10836     case CA_SET_PLAYER_GRAVITY:
10837     {
10838       for (i = 0; i < MAX_PLAYERS; i++)
10839       {
10840         if (trigger_player_bits & (1 << i))
10841         {
10842           stored_player[i].gravity =
10843             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10844              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10845              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10846              stored_player[i].gravity);
10847         }
10848       }
10849
10850       break;
10851     }
10852 #endif
10853
10854     case CA_SET_PLAYER_ARTWORK:
10855     {
10856       for (i = 0; i < MAX_PLAYERS; i++)
10857       {
10858         if (trigger_player_bits & (1 << i))
10859         {
10860           int artwork_element = action_arg_element;
10861
10862           if (action_arg == CA_ARG_ELEMENT_RESET)
10863             artwork_element =
10864               (level.use_artwork_element[i] ? level.artwork_element[i] :
10865                stored_player[i].element_nr);
10866
10867 #if USE_GFX_RESET_PLAYER_ARTWORK
10868           if (stored_player[i].artwork_element != artwork_element)
10869             stored_player[i].Frame = 0;
10870 #endif
10871
10872           stored_player[i].artwork_element = artwork_element;
10873
10874           SetPlayerWaiting(&stored_player[i], FALSE);
10875
10876           /* set number of special actions for bored and sleeping animation */
10877           stored_player[i].num_special_action_bored =
10878             get_num_special_action(artwork_element,
10879                                    ACTION_BORING_1, ACTION_BORING_LAST);
10880           stored_player[i].num_special_action_sleeping =
10881             get_num_special_action(artwork_element,
10882                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10883         }
10884       }
10885
10886       break;
10887     }
10888
10889     case CA_SET_PLAYER_INVENTORY:
10890     {
10891       for (i = 0; i < MAX_PLAYERS; i++)
10892       {
10893         struct PlayerInfo *player = &stored_player[i];
10894         int j, k;
10895
10896         if (trigger_player_bits & (1 << i))
10897         {
10898           int inventory_element = action_arg_element;
10899
10900           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10901               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10902               action_arg == CA_ARG_ELEMENT_ACTION)
10903           {
10904             int element = inventory_element;
10905             int collect_count = element_info[element].collect_count_initial;
10906
10907             if (!IS_CUSTOM_ELEMENT(element))
10908               collect_count = 1;
10909
10910             if (collect_count == 0)
10911               player->inventory_infinite_element = element;
10912             else
10913               for (k = 0; k < collect_count; k++)
10914                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10915                   player->inventory_element[player->inventory_size++] =
10916                     element;
10917           }
10918           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10919                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10920                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10921           {
10922             if (player->inventory_infinite_element != EL_UNDEFINED &&
10923                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10924                                      action_arg_element_raw))
10925               player->inventory_infinite_element = EL_UNDEFINED;
10926
10927             for (k = 0, j = 0; j < player->inventory_size; j++)
10928             {
10929               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10930                                         action_arg_element_raw))
10931                 player->inventory_element[k++] = player->inventory_element[j];
10932             }
10933
10934             player->inventory_size = k;
10935           }
10936           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10937           {
10938             if (player->inventory_size > 0)
10939             {
10940               for (j = 0; j < player->inventory_size - 1; j++)
10941                 player->inventory_element[j] = player->inventory_element[j + 1];
10942
10943               player->inventory_size--;
10944             }
10945           }
10946           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10947           {
10948             if (player->inventory_size > 0)
10949               player->inventory_size--;
10950           }
10951           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10952           {
10953             player->inventory_infinite_element = EL_UNDEFINED;
10954             player->inventory_size = 0;
10955           }
10956           else if (action_arg == CA_ARG_INVENTORY_RESET)
10957           {
10958             player->inventory_infinite_element = EL_UNDEFINED;
10959             player->inventory_size = 0;
10960
10961             if (level.use_initial_inventory[i])
10962             {
10963               for (j = 0; j < level.initial_inventory_size[i]; j++)
10964               {
10965                 int element = level.initial_inventory_content[i][j];
10966                 int collect_count = element_info[element].collect_count_initial;
10967
10968                 if (!IS_CUSTOM_ELEMENT(element))
10969                   collect_count = 1;
10970
10971                 if (collect_count == 0)
10972                   player->inventory_infinite_element = element;
10973                 else
10974                   for (k = 0; k < collect_count; k++)
10975                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10976                       player->inventory_element[player->inventory_size++] =
10977                         element;
10978               }
10979             }
10980           }
10981         }
10982       }
10983
10984       break;
10985     }
10986
10987     /* ---------- CE actions  ---------------------------------------------- */
10988
10989     case CA_SET_CE_VALUE:
10990     {
10991 #if USE_NEW_CUSTOM_VALUE
10992       int last_ce_value = CustomValue[x][y];
10993
10994       CustomValue[x][y] = action_arg_number_new;
10995
10996       if (CustomValue[x][y] != last_ce_value)
10997       {
10998         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10999         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11000
11001         if (CustomValue[x][y] == 0)
11002         {
11003           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11004           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11005         }
11006       }
11007 #endif
11008
11009       break;
11010     }
11011
11012     case CA_SET_CE_SCORE:
11013     {
11014 #if USE_NEW_CUSTOM_VALUE
11015       int last_ce_score = ei->collect_score;
11016
11017       ei->collect_score = action_arg_number_new;
11018
11019       if (ei->collect_score != last_ce_score)
11020       {
11021         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11022         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11023
11024         if (ei->collect_score == 0)
11025         {
11026           int xx, yy;
11027
11028           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11029           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11030
11031           /*
11032             This is a very special case that seems to be a mixture between
11033             CheckElementChange() and CheckTriggeredElementChange(): while
11034             the first one only affects single elements that are triggered
11035             directly, the second one affects multiple elements in the playfield
11036             that are triggered indirectly by another element. This is a third
11037             case: Changing the CE score always affects multiple identical CEs,
11038             so every affected CE must be checked, not only the single CE for
11039             which the CE score was changed in the first place (as every instance
11040             of that CE shares the same CE score, and therefore also can change)!
11041           */
11042           SCAN_PLAYFIELD(xx, yy)
11043           {
11044             if (Feld[xx][yy] == element)
11045               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11046                                  CE_SCORE_GETS_ZERO);
11047           }
11048         }
11049       }
11050 #endif
11051
11052       break;
11053     }
11054
11055     case CA_SET_CE_ARTWORK:
11056     {
11057       int artwork_element = action_arg_element;
11058       boolean reset_frame = FALSE;
11059       int xx, yy;
11060
11061       if (action_arg == CA_ARG_ELEMENT_RESET)
11062         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11063                            element);
11064
11065       if (ei->gfx_element != artwork_element)
11066         reset_frame = TRUE;
11067
11068       ei->gfx_element = artwork_element;
11069
11070       SCAN_PLAYFIELD(xx, yy)
11071       {
11072         if (Feld[xx][yy] == element)
11073         {
11074           if (reset_frame)
11075           {
11076             ResetGfxAnimation(xx, yy);
11077             ResetRandomAnimationValue(xx, yy);
11078           }
11079
11080           TEST_DrawLevelField(xx, yy);
11081         }
11082       }
11083
11084       break;
11085     }
11086
11087     /* ---------- engine actions  ------------------------------------------ */
11088
11089     case CA_SET_ENGINE_SCAN_MODE:
11090     {
11091       InitPlayfieldScanMode(action_arg);
11092
11093       break;
11094     }
11095
11096     default:
11097       break;
11098   }
11099 }
11100
11101 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11102 {
11103   int old_element = Feld[x][y];
11104   int new_element = GetElementFromGroupElement(element);
11105   int previous_move_direction = MovDir[x][y];
11106 #if USE_NEW_CUSTOM_VALUE
11107   int last_ce_value = CustomValue[x][y];
11108 #endif
11109   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11110   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11111   boolean add_player_onto_element = (new_element_is_player &&
11112 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11113                                      /* this breaks SnakeBite when a snake is
11114                                         halfway through a door that closes */
11115                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11116                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11117 #endif
11118                                      IS_WALKABLE(old_element));
11119
11120 #if 0
11121   /* check if element under the player changes from accessible to unaccessible
11122      (needed for special case of dropping element which then changes) */
11123   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11124       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11125   {
11126     Bang(x, y);
11127
11128     return;
11129   }
11130 #endif
11131
11132   if (!add_player_onto_element)
11133   {
11134     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11135       RemoveMovingField(x, y);
11136     else
11137       RemoveField(x, y);
11138
11139     Feld[x][y] = new_element;
11140
11141 #if !USE_GFX_RESET_GFX_ANIMATION
11142     ResetGfxAnimation(x, y);
11143     ResetRandomAnimationValue(x, y);
11144 #endif
11145
11146     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11147       MovDir[x][y] = previous_move_direction;
11148
11149 #if USE_NEW_CUSTOM_VALUE
11150     if (element_info[new_element].use_last_ce_value)
11151       CustomValue[x][y] = last_ce_value;
11152 #endif
11153
11154     InitField_WithBug1(x, y, FALSE);
11155
11156     new_element = Feld[x][y];   /* element may have changed */
11157
11158 #if USE_GFX_RESET_GFX_ANIMATION
11159     ResetGfxAnimation(x, y);
11160     ResetRandomAnimationValue(x, y);
11161 #endif
11162
11163     TEST_DrawLevelField(x, y);
11164
11165     if (GFX_CRUMBLED(new_element))
11166       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11167   }
11168
11169 #if 1
11170   /* check if element under the player changes from accessible to unaccessible
11171      (needed for special case of dropping element which then changes) */
11172   /* (must be checked after creating new element for walkable group elements) */
11173 #if USE_FIX_KILLED_BY_NON_WALKABLE
11174   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11175       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11176   {
11177     Bang(x, y);
11178
11179     return;
11180   }
11181 #else
11182   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11183       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11184   {
11185     Bang(x, y);
11186
11187     return;
11188   }
11189 #endif
11190 #endif
11191
11192   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11193   if (new_element_is_player)
11194     RelocatePlayer(x, y, new_element);
11195
11196   if (is_change)
11197     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11198
11199   TestIfBadThingTouchesPlayer(x, y);
11200   TestIfPlayerTouchesCustomElement(x, y);
11201   TestIfElementTouchesCustomElement(x, y);
11202 }
11203
11204 static void CreateField(int x, int y, int element)
11205 {
11206   CreateFieldExt(x, y, element, FALSE);
11207 }
11208
11209 static void CreateElementFromChange(int x, int y, int element)
11210 {
11211   element = GET_VALID_RUNTIME_ELEMENT(element);
11212
11213 #if USE_STOP_CHANGED_ELEMENTS
11214   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11215   {
11216     int old_element = Feld[x][y];
11217
11218     /* prevent changed element from moving in same engine frame
11219        unless both old and new element can either fall or move */
11220     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11221         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11222       Stop[x][y] = TRUE;
11223   }
11224 #endif
11225
11226   CreateFieldExt(x, y, element, TRUE);
11227 }
11228
11229 static boolean ChangeElement(int x, int y, int element, int page)
11230 {
11231   struct ElementInfo *ei = &element_info[element];
11232   struct ElementChangeInfo *change = &ei->change_page[page];
11233   int ce_value = CustomValue[x][y];
11234   int ce_score = ei->collect_score;
11235   int target_element;
11236   int old_element = Feld[x][y];
11237
11238   /* always use default change event to prevent running into a loop */
11239   if (ChangeEvent[x][y] == -1)
11240     ChangeEvent[x][y] = CE_DELAY;
11241
11242   if (ChangeEvent[x][y] == CE_DELAY)
11243   {
11244     /* reset actual trigger element, trigger player and action element */
11245     change->actual_trigger_element = EL_EMPTY;
11246     change->actual_trigger_player = EL_EMPTY;
11247     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11248     change->actual_trigger_side = CH_SIDE_NONE;
11249     change->actual_trigger_ce_value = 0;
11250     change->actual_trigger_ce_score = 0;
11251   }
11252
11253   /* do not change elements more than a specified maximum number of changes */
11254   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11255     return FALSE;
11256
11257   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11258
11259   if (change->explode)
11260   {
11261     Bang(x, y);
11262
11263     return TRUE;
11264   }
11265
11266   if (change->use_target_content)
11267   {
11268     boolean complete_replace = TRUE;
11269     boolean can_replace[3][3];
11270     int xx, yy;
11271
11272     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11273     {
11274       boolean is_empty;
11275       boolean is_walkable;
11276       boolean is_diggable;
11277       boolean is_collectible;
11278       boolean is_removable;
11279       boolean is_destructible;
11280       int ex = x + xx - 1;
11281       int ey = y + yy - 1;
11282       int content_element = change->target_content.e[xx][yy];
11283       int e;
11284
11285       can_replace[xx][yy] = TRUE;
11286
11287       if (ex == x && ey == y)   /* do not check changing element itself */
11288         continue;
11289
11290       if (content_element == EL_EMPTY_SPACE)
11291       {
11292         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11293
11294         continue;
11295       }
11296
11297       if (!IN_LEV_FIELD(ex, ey))
11298       {
11299         can_replace[xx][yy] = FALSE;
11300         complete_replace = FALSE;
11301
11302         continue;
11303       }
11304
11305       e = Feld[ex][ey];
11306
11307       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11308         e = MovingOrBlocked2Element(ex, ey);
11309
11310       is_empty = (IS_FREE(ex, ey) ||
11311                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11312
11313       is_walkable     = (is_empty || IS_WALKABLE(e));
11314       is_diggable     = (is_empty || IS_DIGGABLE(e));
11315       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11316       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11317       is_removable    = (is_diggable || is_collectible);
11318
11319       can_replace[xx][yy] =
11320         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11321           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11322           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11323           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11324           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11325           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11326          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11327
11328       if (!can_replace[xx][yy])
11329         complete_replace = FALSE;
11330     }
11331
11332     if (!change->only_if_complete || complete_replace)
11333     {
11334       boolean something_has_changed = FALSE;
11335
11336       if (change->only_if_complete && change->use_random_replace &&
11337           RND(100) < change->random_percentage)
11338         return FALSE;
11339
11340       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11341       {
11342         int ex = x + xx - 1;
11343         int ey = y + yy - 1;
11344         int content_element;
11345
11346         if (can_replace[xx][yy] && (!change->use_random_replace ||
11347                                     RND(100) < change->random_percentage))
11348         {
11349           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11350             RemoveMovingField(ex, ey);
11351
11352           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11353
11354           content_element = change->target_content.e[xx][yy];
11355           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11356                                               ce_value, ce_score);
11357
11358           CreateElementFromChange(ex, ey, target_element);
11359
11360           something_has_changed = TRUE;
11361
11362           /* for symmetry reasons, freeze newly created border elements */
11363           if (ex != x || ey != y)
11364             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11365         }
11366       }
11367
11368       if (something_has_changed)
11369       {
11370         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11371         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11372       }
11373     }
11374   }
11375   else
11376   {
11377     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11378                                         ce_value, ce_score);
11379
11380     if (element == EL_DIAGONAL_GROWING ||
11381         element == EL_DIAGONAL_SHRINKING)
11382     {
11383       target_element = Store[x][y];
11384
11385       Store[x][y] = EL_EMPTY;
11386     }
11387
11388     CreateElementFromChange(x, y, target_element);
11389
11390     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11391     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11392   }
11393
11394   /* this uses direct change before indirect change */
11395   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11396
11397   return TRUE;
11398 }
11399
11400 #if USE_NEW_DELAYED_ACTION
11401
11402 static void HandleElementChange(int x, int y, int page)
11403 {
11404   int element = MovingOrBlocked2Element(x, y);
11405   struct ElementInfo *ei = &element_info[element];
11406   struct ElementChangeInfo *change = &ei->change_page[page];
11407   boolean handle_action_before_change = FALSE;
11408
11409 #ifdef DEBUG
11410   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11411       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11412   {
11413     printf("\n\n");
11414     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11415            x, y, element, element_info[element].token_name);
11416     printf("HandleElementChange(): This should never happen!\n");
11417     printf("\n\n");
11418   }
11419 #endif
11420
11421   /* this can happen with classic bombs on walkable, changing elements */
11422   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11423   {
11424 #if 0
11425     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11426       ChangeDelay[x][y] = 0;
11427 #endif
11428
11429     return;
11430   }
11431
11432   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11433   {
11434     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11435
11436     if (change->can_change)
11437     {
11438 #if 1
11439       /* !!! not clear why graphic animation should be reset at all here !!! */
11440       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11441 #if USE_GFX_RESET_WHEN_NOT_MOVING
11442       /* when a custom element is about to change (for example by change delay),
11443          do not reset graphic animation when the custom element is moving */
11444       if (!IS_MOVING(x, y))
11445 #endif
11446       {
11447         ResetGfxAnimation(x, y);
11448         ResetRandomAnimationValue(x, y);
11449       }
11450 #endif
11451
11452       if (change->pre_change_function)
11453         change->pre_change_function(x, y);
11454     }
11455   }
11456
11457   ChangeDelay[x][y]--;
11458
11459   if (ChangeDelay[x][y] != 0)           /* continue element change */
11460   {
11461     if (change->can_change)
11462     {
11463       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11464
11465       if (IS_ANIMATED(graphic))
11466         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11467
11468       if (change->change_function)
11469         change->change_function(x, y);
11470     }
11471   }
11472   else                                  /* finish element change */
11473   {
11474     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11475     {
11476       page = ChangePage[x][y];
11477       ChangePage[x][y] = -1;
11478
11479       change = &ei->change_page[page];
11480     }
11481
11482     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11483     {
11484       ChangeDelay[x][y] = 1;            /* try change after next move step */
11485       ChangePage[x][y] = page;          /* remember page to use for change */
11486
11487       return;
11488     }
11489
11490 #if 1
11491     /* special case: set new level random seed before changing element */
11492     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11493       handle_action_before_change = TRUE;
11494
11495     if (change->has_action && handle_action_before_change)
11496       ExecuteCustomElementAction(x, y, element, page);
11497 #endif
11498
11499     if (change->can_change)
11500     {
11501       if (ChangeElement(x, y, element, page))
11502       {
11503         if (change->post_change_function)
11504           change->post_change_function(x, y);
11505       }
11506     }
11507
11508     if (change->has_action && !handle_action_before_change)
11509       ExecuteCustomElementAction(x, y, element, page);
11510   }
11511 }
11512
11513 #else
11514
11515 static void HandleElementChange(int x, int y, int page)
11516 {
11517   int element = MovingOrBlocked2Element(x, y);
11518   struct ElementInfo *ei = &element_info[element];
11519   struct ElementChangeInfo *change = &ei->change_page[page];
11520
11521 #ifdef DEBUG
11522   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11523   {
11524     printf("\n\n");
11525     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11526            x, y, element, element_info[element].token_name);
11527     printf("HandleElementChange(): This should never happen!\n");
11528     printf("\n\n");
11529   }
11530 #endif
11531
11532   /* this can happen with classic bombs on walkable, changing elements */
11533   if (!CAN_CHANGE(element))
11534   {
11535 #if 0
11536     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11537       ChangeDelay[x][y] = 0;
11538 #endif
11539
11540     return;
11541   }
11542
11543   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11544   {
11545     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11546
11547     ResetGfxAnimation(x, y);
11548     ResetRandomAnimationValue(x, y);
11549
11550     if (change->pre_change_function)
11551       change->pre_change_function(x, y);
11552   }
11553
11554   ChangeDelay[x][y]--;
11555
11556   if (ChangeDelay[x][y] != 0)           /* continue element change */
11557   {
11558     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11559
11560     if (IS_ANIMATED(graphic))
11561       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11562
11563     if (change->change_function)
11564       change->change_function(x, y);
11565   }
11566   else                                  /* finish element change */
11567   {
11568     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11569     {
11570       page = ChangePage[x][y];
11571       ChangePage[x][y] = -1;
11572
11573       change = &ei->change_page[page];
11574     }
11575
11576     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11577     {
11578       ChangeDelay[x][y] = 1;            /* try change after next move step */
11579       ChangePage[x][y] = page;          /* remember page to use for change */
11580
11581       return;
11582     }
11583
11584     if (ChangeElement(x, y, element, page))
11585     {
11586       if (change->post_change_function)
11587         change->post_change_function(x, y);
11588     }
11589   }
11590 }
11591
11592 #endif
11593
11594 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11595                                               int trigger_element,
11596                                               int trigger_event,
11597                                               int trigger_player,
11598                                               int trigger_side,
11599                                               int trigger_page)
11600 {
11601   boolean change_done_any = FALSE;
11602   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11603   int i;
11604
11605   if (!(trigger_events[trigger_element][trigger_event]))
11606     return FALSE;
11607
11608 #if 0
11609   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11610          trigger_event, recursion_loop_depth, recursion_loop_detected,
11611          recursion_loop_element, EL_NAME(recursion_loop_element));
11612 #endif
11613
11614   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11615
11616   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11617   {
11618     int element = EL_CUSTOM_START + i;
11619     boolean change_done = FALSE;
11620     int p;
11621
11622     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11623         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11624       continue;
11625
11626     for (p = 0; p < element_info[element].num_change_pages; p++)
11627     {
11628       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11629
11630       if (change->can_change_or_has_action &&
11631           change->has_event[trigger_event] &&
11632           change->trigger_side & trigger_side &&
11633           change->trigger_player & trigger_player &&
11634           change->trigger_page & trigger_page_bits &&
11635           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11636       {
11637         change->actual_trigger_element = trigger_element;
11638         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11639         change->actual_trigger_player_bits = trigger_player;
11640         change->actual_trigger_side = trigger_side;
11641         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11642         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11643
11644 #if 0
11645         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11646                element, EL_NAME(element), p);
11647 #endif
11648
11649         if ((change->can_change && !change_done) || change->has_action)
11650         {
11651           int x, y;
11652
11653           SCAN_PLAYFIELD(x, y)
11654           {
11655             if (Feld[x][y] == element)
11656             {
11657               if (change->can_change && !change_done)
11658               {
11659 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11660                 /* if element already changed in this frame, not only prevent
11661                    another element change (checked in ChangeElement()), but
11662                    also prevent additional element actions for this element */
11663
11664                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11665                     !level.use_action_after_change_bug)
11666                   continue;
11667 #endif
11668
11669 #if 0
11670                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11671                        element, EL_NAME(element), p);
11672 #endif
11673
11674                 ChangeDelay[x][y] = 1;
11675                 ChangeEvent[x][y] = trigger_event;
11676
11677                 HandleElementChange(x, y, p);
11678               }
11679 #if USE_NEW_DELAYED_ACTION
11680               else if (change->has_action)
11681               {
11682 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11683                 /* if element already changed in this frame, not only prevent
11684                    another element change (checked in ChangeElement()), but
11685                    also prevent additional element actions for this element */
11686
11687                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11688                     !level.use_action_after_change_bug)
11689                   continue;
11690 #endif
11691
11692
11693 #if 0
11694                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11695                        element, EL_NAME(element), p);
11696 #endif
11697
11698                 ExecuteCustomElementAction(x, y, element, p);
11699                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11700               }
11701 #else
11702               if (change->has_action)
11703               {
11704                 ExecuteCustomElementAction(x, y, element, p);
11705                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11706               }
11707 #endif
11708             }
11709           }
11710
11711           if (change->can_change)
11712           {
11713             change_done = TRUE;
11714             change_done_any = TRUE;
11715
11716 #if 0
11717             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11718                    element, EL_NAME(element), p);
11719 #endif
11720
11721           }
11722         }
11723       }
11724     }
11725   }
11726
11727   RECURSION_LOOP_DETECTION_END();
11728
11729   return change_done_any;
11730 }
11731
11732 static boolean CheckElementChangeExt(int x, int y,
11733                                      int element,
11734                                      int trigger_element,
11735                                      int trigger_event,
11736                                      int trigger_player,
11737                                      int trigger_side)
11738 {
11739   boolean change_done = FALSE;
11740   int p;
11741
11742   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11743       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11744     return FALSE;
11745
11746   if (Feld[x][y] == EL_BLOCKED)
11747   {
11748     Blocked2Moving(x, y, &x, &y);
11749     element = Feld[x][y];
11750   }
11751
11752 #if 0
11753   /* check if element has already changed */
11754   if (Feld[x][y] != element)
11755     return FALSE;
11756 #else
11757   /* check if element has already changed or is about to change after moving */
11758   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11759        Feld[x][y] != element) ||
11760
11761       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11762        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11763         ChangePage[x][y] != -1)))
11764     return FALSE;
11765 #endif
11766
11767 #if 0
11768   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11769          trigger_event, recursion_loop_depth, recursion_loop_detected,
11770          recursion_loop_element, EL_NAME(recursion_loop_element));
11771 #endif
11772
11773   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11774
11775 #if 0
11776   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11777 #endif
11778
11779   for (p = 0; p < element_info[element].num_change_pages; p++)
11780   {
11781     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11782
11783     /* check trigger element for all events where the element that is checked
11784        for changing interacts with a directly adjacent element -- this is
11785        different to element changes that affect other elements to change on the
11786        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11787     boolean check_trigger_element =
11788       (trigger_event == CE_TOUCHING_X ||
11789        trigger_event == CE_HITTING_X ||
11790        trigger_event == CE_HIT_BY_X ||
11791 #if 1
11792        /* this one was forgotten until 3.2.3 */
11793        trigger_event == CE_DIGGING_X);
11794 #endif
11795
11796     if (change->can_change_or_has_action &&
11797         change->has_event[trigger_event] &&
11798         change->trigger_side & trigger_side &&
11799         change->trigger_player & trigger_player &&
11800         (!check_trigger_element ||
11801          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11802     {
11803       change->actual_trigger_element = trigger_element;
11804       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11805       change->actual_trigger_player_bits = trigger_player;
11806       change->actual_trigger_side = trigger_side;
11807       change->actual_trigger_ce_value = CustomValue[x][y];
11808       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11809
11810       /* special case: trigger element not at (x,y) position for some events */
11811       if (check_trigger_element)
11812       {
11813         static struct
11814         {
11815           int dx, dy;
11816         } move_xy[] =
11817           {
11818             {  0,  0 },
11819             { -1,  0 },
11820             { +1,  0 },
11821             {  0,  0 },
11822             {  0, -1 },
11823             {  0,  0 }, { 0, 0 }, { 0, 0 },
11824             {  0, +1 }
11825           };
11826
11827         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11828         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11829
11830         change->actual_trigger_ce_value = CustomValue[xx][yy];
11831         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11832       }
11833
11834       if (change->can_change && !change_done)
11835       {
11836         ChangeDelay[x][y] = 1;
11837         ChangeEvent[x][y] = trigger_event;
11838
11839         HandleElementChange(x, y, p);
11840
11841         change_done = TRUE;
11842       }
11843 #if USE_NEW_DELAYED_ACTION
11844       else if (change->has_action)
11845       {
11846         ExecuteCustomElementAction(x, y, element, p);
11847         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11848       }
11849 #else
11850       if (change->has_action)
11851       {
11852         ExecuteCustomElementAction(x, y, element, p);
11853         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11854       }
11855 #endif
11856     }
11857   }
11858
11859   RECURSION_LOOP_DETECTION_END();
11860
11861   return change_done;
11862 }
11863
11864 static void PlayPlayerSound(struct PlayerInfo *player)
11865 {
11866   int jx = player->jx, jy = player->jy;
11867   int sound_element = player->artwork_element;
11868   int last_action = player->last_action_waiting;
11869   int action = player->action_waiting;
11870
11871   if (player->is_waiting)
11872   {
11873     if (action != last_action)
11874       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11875     else
11876       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11877   }
11878   else
11879   {
11880     if (action != last_action)
11881       StopSound(element_info[sound_element].sound[last_action]);
11882
11883     if (last_action == ACTION_SLEEPING)
11884       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11885   }
11886 }
11887
11888 static void PlayAllPlayersSound()
11889 {
11890   int i;
11891
11892   for (i = 0; i < MAX_PLAYERS; i++)
11893     if (stored_player[i].active)
11894       PlayPlayerSound(&stored_player[i]);
11895 }
11896
11897 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11898 {
11899   boolean last_waiting = player->is_waiting;
11900   int move_dir = player->MovDir;
11901
11902   player->dir_waiting = move_dir;
11903   player->last_action_waiting = player->action_waiting;
11904
11905   if (is_waiting)
11906   {
11907     if (!last_waiting)          /* not waiting -> waiting */
11908     {
11909       player->is_waiting = TRUE;
11910
11911       player->frame_counter_bored =
11912         FrameCounter +
11913         game.player_boring_delay_fixed +
11914         GetSimpleRandom(game.player_boring_delay_random);
11915       player->frame_counter_sleeping =
11916         FrameCounter +
11917         game.player_sleeping_delay_fixed +
11918         GetSimpleRandom(game.player_sleeping_delay_random);
11919
11920       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11921     }
11922
11923     if (game.player_sleeping_delay_fixed +
11924         game.player_sleeping_delay_random > 0 &&
11925         player->anim_delay_counter == 0 &&
11926         player->post_delay_counter == 0 &&
11927         FrameCounter >= player->frame_counter_sleeping)
11928       player->is_sleeping = TRUE;
11929     else if (game.player_boring_delay_fixed +
11930              game.player_boring_delay_random > 0 &&
11931              FrameCounter >= player->frame_counter_bored)
11932       player->is_bored = TRUE;
11933
11934     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11935                               player->is_bored ? ACTION_BORING :
11936                               ACTION_WAITING);
11937
11938     if (player->is_sleeping && player->use_murphy)
11939     {
11940       /* special case for sleeping Murphy when leaning against non-free tile */
11941
11942       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11943           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11944            !IS_MOVING(player->jx - 1, player->jy)))
11945         move_dir = MV_LEFT;
11946       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11947                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11948                 !IS_MOVING(player->jx + 1, player->jy)))
11949         move_dir = MV_RIGHT;
11950       else
11951         player->is_sleeping = FALSE;
11952
11953       player->dir_waiting = move_dir;
11954     }
11955
11956     if (player->is_sleeping)
11957     {
11958       if (player->num_special_action_sleeping > 0)
11959       {
11960         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11961         {
11962           int last_special_action = player->special_action_sleeping;
11963           int num_special_action = player->num_special_action_sleeping;
11964           int special_action =
11965             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11966              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11967              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11968              last_special_action + 1 : ACTION_SLEEPING);
11969           int special_graphic =
11970             el_act_dir2img(player->artwork_element, special_action, move_dir);
11971
11972           player->anim_delay_counter =
11973             graphic_info[special_graphic].anim_delay_fixed +
11974             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11975           player->post_delay_counter =
11976             graphic_info[special_graphic].post_delay_fixed +
11977             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11978
11979           player->special_action_sleeping = special_action;
11980         }
11981
11982         if (player->anim_delay_counter > 0)
11983         {
11984           player->action_waiting = player->special_action_sleeping;
11985           player->anim_delay_counter--;
11986         }
11987         else if (player->post_delay_counter > 0)
11988         {
11989           player->post_delay_counter--;
11990         }
11991       }
11992     }
11993     else if (player->is_bored)
11994     {
11995       if (player->num_special_action_bored > 0)
11996       {
11997         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11998         {
11999           int special_action =
12000             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12001           int special_graphic =
12002             el_act_dir2img(player->artwork_element, special_action, move_dir);
12003
12004           player->anim_delay_counter =
12005             graphic_info[special_graphic].anim_delay_fixed +
12006             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12007           player->post_delay_counter =
12008             graphic_info[special_graphic].post_delay_fixed +
12009             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12010
12011           player->special_action_bored = special_action;
12012         }
12013
12014         if (player->anim_delay_counter > 0)
12015         {
12016           player->action_waiting = player->special_action_bored;
12017           player->anim_delay_counter--;
12018         }
12019         else if (player->post_delay_counter > 0)
12020         {
12021           player->post_delay_counter--;
12022         }
12023       }
12024     }
12025   }
12026   else if (last_waiting)        /* waiting -> not waiting */
12027   {
12028     player->is_waiting = FALSE;
12029     player->is_bored = FALSE;
12030     player->is_sleeping = FALSE;
12031
12032     player->frame_counter_bored = -1;
12033     player->frame_counter_sleeping = -1;
12034
12035     player->anim_delay_counter = 0;
12036     player->post_delay_counter = 0;
12037
12038     player->dir_waiting = player->MovDir;
12039     player->action_waiting = ACTION_DEFAULT;
12040
12041     player->special_action_bored = ACTION_DEFAULT;
12042     player->special_action_sleeping = ACTION_DEFAULT;
12043   }
12044 }
12045
12046 static void CheckSingleStepMode(struct PlayerInfo *player)
12047 {
12048   if (tape.single_step && tape.recording && !tape.pausing)
12049   {
12050     /* as it is called "single step mode", just return to pause mode when the
12051        player stopped moving after one tile (or never starts moving at all) */
12052     if (!player->is_moving && !player->is_pushing)
12053     {
12054       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12055       SnapField(player, 0, 0);                  /* stop snapping */
12056     }
12057   }
12058 }
12059
12060 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12061 {
12062   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12063   int left      = player_action & JOY_LEFT;
12064   int right     = player_action & JOY_RIGHT;
12065   int up        = player_action & JOY_UP;
12066   int down      = player_action & JOY_DOWN;
12067   int button1   = player_action & JOY_BUTTON_1;
12068   int button2   = player_action & JOY_BUTTON_2;
12069   int dx        = (left ? -1 : right ? 1 : 0);
12070   int dy        = (up   ? -1 : down  ? 1 : 0);
12071
12072   if (!player->active || tape.pausing)
12073     return 0;
12074
12075   if (player_action)
12076   {
12077     if (button1)
12078       snapped = SnapField(player, dx, dy);
12079     else
12080     {
12081       if (button2)
12082         dropped = DropElement(player);
12083
12084       moved = MovePlayer(player, dx, dy);
12085     }
12086
12087     CheckSingleStepMode(player);
12088
12089     SetPlayerWaiting(player, FALSE);
12090
12091     return player_action;
12092   }
12093   else
12094   {
12095     /* no actions for this player (no input at player's configured device) */
12096
12097     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12098     SnapField(player, 0, 0);
12099     CheckGravityMovementWhenNotMoving(player);
12100
12101     if (player->MovPos == 0)
12102       SetPlayerWaiting(player, TRUE);
12103
12104     if (player->MovPos == 0)    /* needed for tape.playing */
12105       player->is_moving = FALSE;
12106
12107     player->is_dropping = FALSE;
12108     player->is_dropping_pressed = FALSE;
12109     player->drop_pressed_delay = 0;
12110
12111     CheckSingleStepMode(player);
12112
12113     return 0;
12114   }
12115 }
12116
12117 static void CheckLevelTime()
12118 {
12119   int i;
12120
12121   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12122   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12123   {
12124     if (level.native_em_level->lev->home == 0)  /* all players at home */
12125     {
12126       PlayerWins(local_player);
12127
12128       AllPlayersGone = TRUE;
12129
12130       level.native_em_level->lev->home = -1;
12131     }
12132
12133     if (level.native_em_level->ply[0]->alive == 0 &&
12134         level.native_em_level->ply[1]->alive == 0 &&
12135         level.native_em_level->ply[2]->alive == 0 &&
12136         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12137       AllPlayersGone = TRUE;
12138   }
12139   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12140   {
12141     if (game_sp.LevelSolved &&
12142         !game_sp.GameOver)                              /* game won */
12143     {
12144       PlayerWins(local_player);
12145
12146       game_sp.GameOver = TRUE;
12147
12148       AllPlayersGone = TRUE;
12149     }
12150
12151     if (game_sp.GameOver)                               /* game lost */
12152       AllPlayersGone = TRUE;
12153   }
12154
12155   if (TimeFrames >= FRAMES_PER_SECOND)
12156   {
12157     TimeFrames = 0;
12158     TapeTime++;
12159
12160     for (i = 0; i < MAX_PLAYERS; i++)
12161     {
12162       struct PlayerInfo *player = &stored_player[i];
12163
12164       if (SHIELD_ON(player))
12165       {
12166         player->shield_normal_time_left--;
12167
12168         if (player->shield_deadly_time_left > 0)
12169           player->shield_deadly_time_left--;
12170       }
12171     }
12172
12173     if (!local_player->LevelSolved && !level.use_step_counter)
12174     {
12175       TimePlayed++;
12176
12177       if (TimeLeft > 0)
12178       {
12179         TimeLeft--;
12180
12181         if (TimeLeft <= 10 && setup.time_limit)
12182           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12183
12184 #if 1
12185         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12186
12187         DisplayGameControlValues();
12188 #else
12189         DrawGameValue_Time(TimeLeft);
12190 #endif
12191
12192         if (!TimeLeft && setup.time_limit)
12193         {
12194           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12195             level.native_em_level->lev->killed_out_of_time = TRUE;
12196           else
12197             for (i = 0; i < MAX_PLAYERS; i++)
12198               KillPlayer(&stored_player[i]);
12199         }
12200       }
12201 #if 1
12202       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12203       {
12204         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12205
12206         DisplayGameControlValues();
12207       }
12208 #else
12209       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12210         DrawGameValue_Time(TimePlayed);
12211 #endif
12212
12213       level.native_em_level->lev->time =
12214         (level.time == 0 ? TimePlayed : TimeLeft);
12215     }
12216
12217     if (tape.recording || tape.playing)
12218       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12219   }
12220
12221 #if 1
12222   UpdateAndDisplayGameControlValues();
12223 #else
12224   UpdateGameDoorValues();
12225   DrawGameDoorValues();
12226 #endif
12227 }
12228
12229 void AdvanceFrameAndPlayerCounters(int player_nr)
12230 {
12231   int i;
12232
12233   /* advance frame counters (global frame counter and time frame counter) */
12234   FrameCounter++;
12235   TimeFrames++;
12236
12237   /* advance player counters (counters for move delay, move animation etc.) */
12238   for (i = 0; i < MAX_PLAYERS; i++)
12239   {
12240     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12241     int move_delay_value = stored_player[i].move_delay_value;
12242     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12243
12244     if (!advance_player_counters)       /* not all players may be affected */
12245       continue;
12246
12247 #if USE_NEW_PLAYER_ANIM
12248     if (move_frames == 0)       /* less than one move per game frame */
12249     {
12250       int stepsize = TILEX / move_delay_value;
12251       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12252       int count = (stored_player[i].is_moving ?
12253                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12254
12255       if (count % delay == 0)
12256         move_frames = 1;
12257     }
12258 #endif
12259
12260     stored_player[i].Frame += move_frames;
12261
12262     if (stored_player[i].MovPos != 0)
12263       stored_player[i].StepFrame += move_frames;
12264
12265     if (stored_player[i].move_delay > 0)
12266       stored_player[i].move_delay--;
12267
12268     /* due to bugs in previous versions, counter must count up, not down */
12269     if (stored_player[i].push_delay != -1)
12270       stored_player[i].push_delay++;
12271
12272     if (stored_player[i].drop_delay > 0)
12273       stored_player[i].drop_delay--;
12274
12275     if (stored_player[i].is_dropping_pressed)
12276       stored_player[i].drop_pressed_delay++;
12277   }
12278 }
12279
12280 void StartGameActions(boolean init_network_game, boolean record_tape,
12281                       long random_seed)
12282 {
12283   unsigned long new_random_seed = InitRND(random_seed);
12284
12285   if (record_tape)
12286     TapeStartRecording(new_random_seed);
12287
12288 #if defined(NETWORK_AVALIABLE)
12289   if (init_network_game)
12290   {
12291     SendToServer_StartPlaying();
12292
12293     return;
12294   }
12295 #endif
12296
12297   InitGame();
12298 }
12299
12300 void GameActions()
12301 {
12302   static unsigned long game_frame_delay = 0;
12303   unsigned long game_frame_delay_value;
12304   byte *recorded_player_action;
12305   byte summarized_player_action = 0;
12306   byte tape_action[MAX_PLAYERS];
12307   int i;
12308
12309   /* detect endless loops, caused by custom element programming */
12310   if (recursion_loop_detected && recursion_loop_depth == 0)
12311   {
12312     char *message = getStringCat3("Internal Error ! Element ",
12313                                   EL_NAME(recursion_loop_element),
12314                                   " caused endless loop ! Quit the game ?");
12315
12316     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12317           EL_NAME(recursion_loop_element));
12318
12319     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12320
12321     recursion_loop_detected = FALSE;    /* if game should be continued */
12322
12323     free(message);
12324
12325     return;
12326   }
12327
12328   if (game.restart_level)
12329     StartGameActions(options.network, setup.autorecord, level.random_seed);
12330
12331   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12332   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12333   {
12334     if (level.native_em_level->lev->home == 0)  /* all players at home */
12335     {
12336       PlayerWins(local_player);
12337
12338       AllPlayersGone = TRUE;
12339
12340       level.native_em_level->lev->home = -1;
12341     }
12342
12343     if (level.native_em_level->ply[0]->alive == 0 &&
12344         level.native_em_level->ply[1]->alive == 0 &&
12345         level.native_em_level->ply[2]->alive == 0 &&
12346         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12347       AllPlayersGone = TRUE;
12348   }
12349   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12350   {
12351     if (game_sp.LevelSolved &&
12352         !game_sp.GameOver)                              /* game won */
12353     {
12354       PlayerWins(local_player);
12355
12356       game_sp.GameOver = TRUE;
12357
12358       AllPlayersGone = TRUE;
12359     }
12360
12361     if (game_sp.GameOver)                               /* game lost */
12362       AllPlayersGone = TRUE;
12363   }
12364
12365   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12366     GameWon();
12367
12368   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12369     TapeStop();
12370
12371   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12372     return;
12373
12374   game_frame_delay_value =
12375     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12376
12377   if (tape.playing && tape.warp_forward && !tape.pausing)
12378     game_frame_delay_value = 0;
12379
12380   /* ---------- main game synchronization point ---------- */
12381
12382   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12383
12384   if (network_playing && !network_player_action_received)
12385   {
12386     /* try to get network player actions in time */
12387
12388 #if defined(NETWORK_AVALIABLE)
12389     /* last chance to get network player actions without main loop delay */
12390     HandleNetworking();
12391 #endif
12392
12393     /* game was quit by network peer */
12394     if (game_status != GAME_MODE_PLAYING)
12395       return;
12396
12397     if (!network_player_action_received)
12398       return;           /* failed to get network player actions in time */
12399
12400     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12401   }
12402
12403   if (tape.pausing)
12404     return;
12405
12406   /* at this point we know that we really continue executing the game */
12407
12408   network_player_action_received = FALSE;
12409
12410   /* when playing tape, read previously recorded player input from tape data */
12411   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12412
12413 #if 1
12414   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12415   if (tape.pausing)
12416     return;
12417 #endif
12418
12419   if (tape.set_centered_player)
12420   {
12421     game.centered_player_nr_next = tape.centered_player_nr_next;
12422     game.set_centered_player = TRUE;
12423   }
12424
12425   for (i = 0; i < MAX_PLAYERS; i++)
12426   {
12427     summarized_player_action |= stored_player[i].action;
12428
12429     if (!network_playing)
12430       stored_player[i].effective_action = stored_player[i].action;
12431   }
12432
12433 #if defined(NETWORK_AVALIABLE)
12434   if (network_playing)
12435     SendToServer_MovePlayer(summarized_player_action);
12436 #endif
12437
12438   if (!options.network && !setup.team_mode)
12439     local_player->effective_action = summarized_player_action;
12440
12441   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12442   {
12443     for (i = 0; i < MAX_PLAYERS; i++)
12444       stored_player[i].effective_action =
12445         (i == game.centered_player_nr ? summarized_player_action : 0);
12446   }
12447
12448   if (recorded_player_action != NULL)
12449     for (i = 0; i < MAX_PLAYERS; i++)
12450       stored_player[i].effective_action = recorded_player_action[i];
12451
12452   for (i = 0; i < MAX_PLAYERS; i++)
12453   {
12454     tape_action[i] = stored_player[i].effective_action;
12455
12456     /* (this can only happen in the R'n'D game engine) */
12457     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12458       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12459   }
12460
12461   /* only record actions from input devices, but not programmed actions */
12462   if (tape.recording)
12463     TapeRecordAction(tape_action);
12464
12465 #if USE_NEW_PLAYER_ASSIGNMENTS
12466   {
12467     byte mapped_action[MAX_PLAYERS];
12468
12469     for (i = 0; i < MAX_PLAYERS; i++)
12470       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12471
12472     for (i = 0; i < MAX_PLAYERS; i++)
12473       stored_player[i].effective_action = mapped_action[i];
12474   }
12475 #endif
12476
12477   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12478   {
12479     GameActions_EM_Main();
12480   }
12481   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12482   {
12483     GameActions_SP_Main();
12484   }
12485   else
12486   {
12487     GameActions_RND();
12488   }
12489 }
12490
12491 void GameActions_EM_Main()
12492 {
12493   byte effective_action[MAX_PLAYERS];
12494   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12495   int i;
12496
12497   for (i = 0; i < MAX_PLAYERS; i++)
12498     effective_action[i] = stored_player[i].effective_action;
12499
12500   GameActions_EM(effective_action, warp_mode);
12501
12502   CheckLevelTime();
12503
12504   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12505 }
12506
12507 void GameActions_SP_Main()
12508 {
12509   byte effective_action[MAX_PLAYERS];
12510   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12511   int i;
12512
12513   for (i = 0; i < MAX_PLAYERS; i++)
12514     effective_action[i] = stored_player[i].effective_action;
12515
12516   GameActions_SP(effective_action, warp_mode);
12517
12518   CheckLevelTime();
12519
12520   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12521 }
12522
12523 void GameActions_RND()
12524 {
12525   int magic_wall_x = 0, magic_wall_y = 0;
12526   int i, x, y, element, graphic;
12527
12528   InitPlayfieldScanModeVars();
12529
12530 #if USE_ONE_MORE_CHANGE_PER_FRAME
12531   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12532   {
12533     SCAN_PLAYFIELD(x, y)
12534     {
12535       ChangeCount[x][y] = 0;
12536       ChangeEvent[x][y] = -1;
12537     }
12538   }
12539 #endif
12540
12541   if (game.set_centered_player)
12542   {
12543     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12544
12545     /* switching to "all players" only possible if all players fit to screen */
12546     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12547     {
12548       game.centered_player_nr_next = game.centered_player_nr;
12549       game.set_centered_player = FALSE;
12550     }
12551
12552     /* do not switch focus to non-existing (or non-active) player */
12553     if (game.centered_player_nr_next >= 0 &&
12554         !stored_player[game.centered_player_nr_next].active)
12555     {
12556       game.centered_player_nr_next = game.centered_player_nr;
12557       game.set_centered_player = FALSE;
12558     }
12559   }
12560
12561   if (game.set_centered_player &&
12562       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12563   {
12564     int sx, sy;
12565
12566     if (game.centered_player_nr_next == -1)
12567     {
12568       setScreenCenteredToAllPlayers(&sx, &sy);
12569     }
12570     else
12571     {
12572       sx = stored_player[game.centered_player_nr_next].jx;
12573       sy = stored_player[game.centered_player_nr_next].jy;
12574     }
12575
12576     game.centered_player_nr = game.centered_player_nr_next;
12577     game.set_centered_player = FALSE;
12578
12579     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12580     DrawGameDoorValues();
12581   }
12582
12583   for (i = 0; i < MAX_PLAYERS; i++)
12584   {
12585     int actual_player_action = stored_player[i].effective_action;
12586
12587 #if 1
12588     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12589        - rnd_equinox_tetrachloride 048
12590        - rnd_equinox_tetrachloride_ii 096
12591        - rnd_emanuel_schmieg 002
12592        - doctor_sloan_ww 001, 020
12593     */
12594     if (stored_player[i].MovPos == 0)
12595       CheckGravityMovement(&stored_player[i]);
12596 #endif
12597
12598     /* overwrite programmed action with tape action */
12599     if (stored_player[i].programmed_action)
12600       actual_player_action = stored_player[i].programmed_action;
12601
12602     PlayerActions(&stored_player[i], actual_player_action);
12603
12604     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12605   }
12606
12607   ScrollScreen(NULL, SCROLL_GO_ON);
12608
12609   /* for backwards compatibility, the following code emulates a fixed bug that
12610      occured when pushing elements (causing elements that just made their last
12611      pushing step to already (if possible) make their first falling step in the
12612      same game frame, which is bad); this code is also needed to use the famous
12613      "spring push bug" which is used in older levels and might be wanted to be
12614      used also in newer levels, but in this case the buggy pushing code is only
12615      affecting the "spring" element and no other elements */
12616
12617   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12618   {
12619     for (i = 0; i < MAX_PLAYERS; i++)
12620     {
12621       struct PlayerInfo *player = &stored_player[i];
12622       int x = player->jx;
12623       int y = player->jy;
12624
12625       if (player->active && player->is_pushing && player->is_moving &&
12626           IS_MOVING(x, y) &&
12627           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12628            Feld[x][y] == EL_SPRING))
12629       {
12630         ContinueMoving(x, y);
12631
12632         /* continue moving after pushing (this is actually a bug) */
12633         if (!IS_MOVING(x, y))
12634           Stop[x][y] = FALSE;
12635       }
12636     }
12637   }
12638
12639 #if 0
12640   debug_print_timestamp(0, "start main loop profiling");
12641 #endif
12642
12643   SCAN_PLAYFIELD(x, y)
12644   {
12645     ChangeCount[x][y] = 0;
12646     ChangeEvent[x][y] = -1;
12647
12648     /* this must be handled before main playfield loop */
12649     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12650     {
12651       MovDelay[x][y]--;
12652       if (MovDelay[x][y] <= 0)
12653         RemoveField(x, y);
12654     }
12655
12656 #if USE_NEW_SNAP_DELAY
12657     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12658     {
12659       MovDelay[x][y]--;
12660       if (MovDelay[x][y] <= 0)
12661       {
12662         RemoveField(x, y);
12663         TEST_DrawLevelField(x, y);
12664
12665         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12666       }
12667     }
12668 #endif
12669
12670 #if DEBUG
12671     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12672     {
12673       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12674       printf("GameActions(): This should never happen!\n");
12675
12676       ChangePage[x][y] = -1;
12677     }
12678 #endif
12679
12680     Stop[x][y] = FALSE;
12681     if (WasJustMoving[x][y] > 0)
12682       WasJustMoving[x][y]--;
12683     if (WasJustFalling[x][y] > 0)
12684       WasJustFalling[x][y]--;
12685     if (CheckCollision[x][y] > 0)
12686       CheckCollision[x][y]--;
12687     if (CheckImpact[x][y] > 0)
12688       CheckImpact[x][y]--;
12689
12690     GfxFrame[x][y]++;
12691
12692     /* reset finished pushing action (not done in ContinueMoving() to allow
12693        continuous pushing animation for elements with zero push delay) */
12694     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12695     {
12696       ResetGfxAnimation(x, y);
12697       TEST_DrawLevelField(x, y);
12698     }
12699
12700 #if DEBUG
12701     if (IS_BLOCKED(x, y))
12702     {
12703       int oldx, oldy;
12704
12705       Blocked2Moving(x, y, &oldx, &oldy);
12706       if (!IS_MOVING(oldx, oldy))
12707       {
12708         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12709         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12710         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12711         printf("GameActions(): This should never happen!\n");
12712       }
12713     }
12714 #endif
12715   }
12716
12717 #if 0
12718   debug_print_timestamp(0, "- time for pre-main loop:");
12719 #endif
12720
12721 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12722   SCAN_PLAYFIELD(x, y)
12723   {
12724     element = Feld[x][y];
12725     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12726
12727 #if 1
12728     {
12729 #if 1
12730       int element2 = element;
12731       int graphic2 = graphic;
12732 #else
12733       int element2 = Feld[x][y];
12734       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12735 #endif
12736       int last_gfx_frame = GfxFrame[x][y];
12737
12738       if (graphic_info[graphic2].anim_global_sync)
12739         GfxFrame[x][y] = FrameCounter;
12740       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12741         GfxFrame[x][y] = CustomValue[x][y];
12742       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12743         GfxFrame[x][y] = element_info[element2].collect_score;
12744       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12745         GfxFrame[x][y] = ChangeDelay[x][y];
12746
12747       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12748         DrawLevelGraphicAnimation(x, y, graphic2);
12749     }
12750 #else
12751     ResetGfxFrame(x, y, TRUE);
12752 #endif
12753
12754 #if 1
12755     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12756         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12757       ResetRandomAnimationValue(x, y);
12758 #endif
12759
12760 #if 1
12761     SetRandomAnimationValue(x, y);
12762 #endif
12763
12764 #if 1
12765     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12766 #endif
12767   }
12768 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12769
12770 #if 0
12771   debug_print_timestamp(0, "- time for TEST loop:     -->");
12772 #endif
12773
12774   SCAN_PLAYFIELD(x, y)
12775   {
12776     element = Feld[x][y];
12777     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12778
12779     ResetGfxFrame(x, y, TRUE);
12780
12781     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12782         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12783       ResetRandomAnimationValue(x, y);
12784
12785     SetRandomAnimationValue(x, y);
12786
12787     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12788
12789     if (IS_INACTIVE(element))
12790     {
12791       if (IS_ANIMATED(graphic))
12792         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12793
12794       continue;
12795     }
12796
12797     /* this may take place after moving, so 'element' may have changed */
12798     if (IS_CHANGING(x, y) &&
12799         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12800     {
12801       int page = element_info[element].event_page_nr[CE_DELAY];
12802
12803 #if 1
12804       HandleElementChange(x, y, page);
12805 #else
12806       if (CAN_CHANGE(element))
12807         HandleElementChange(x, y, page);
12808
12809       if (HAS_ACTION(element))
12810         ExecuteCustomElementAction(x, y, element, page);
12811 #endif
12812
12813       element = Feld[x][y];
12814       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12815     }
12816
12817 #if 0   // ---------------------------------------------------------------------
12818
12819     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12820     {
12821       StartMoving(x, y);
12822
12823       element = Feld[x][y];
12824       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12825
12826       if (IS_ANIMATED(graphic) &&
12827           !IS_MOVING(x, y) &&
12828           !Stop[x][y])
12829         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12830
12831       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12832         TEST_DrawTwinkleOnField(x, y);
12833     }
12834     else if (IS_MOVING(x, y))
12835       ContinueMoving(x, y);
12836     else
12837     {
12838       switch (element)
12839       {
12840         case EL_ACID:
12841         case EL_EXIT_OPEN:
12842         case EL_EM_EXIT_OPEN:
12843         case EL_SP_EXIT_OPEN:
12844         case EL_STEEL_EXIT_OPEN:
12845         case EL_EM_STEEL_EXIT_OPEN:
12846         case EL_SP_TERMINAL:
12847         case EL_SP_TERMINAL_ACTIVE:
12848         case EL_EXTRA_TIME:
12849         case EL_SHIELD_NORMAL:
12850         case EL_SHIELD_DEADLY:
12851           if (IS_ANIMATED(graphic))
12852             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12853           break;
12854
12855         case EL_DYNAMITE_ACTIVE:
12856         case EL_EM_DYNAMITE_ACTIVE:
12857         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12858         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12859         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12860         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12861         case EL_SP_DISK_RED_ACTIVE:
12862           CheckDynamite(x, y);
12863           break;
12864
12865         case EL_AMOEBA_GROWING:
12866           AmoebeWaechst(x, y);
12867           break;
12868
12869         case EL_AMOEBA_SHRINKING:
12870           AmoebaDisappearing(x, y);
12871           break;
12872
12873 #if !USE_NEW_AMOEBA_CODE
12874         case EL_AMOEBA_WET:
12875         case EL_AMOEBA_DRY:
12876         case EL_AMOEBA_FULL:
12877         case EL_BD_AMOEBA:
12878         case EL_EMC_DRIPPER:
12879           AmoebeAbleger(x, y);
12880           break;
12881 #endif
12882
12883         case EL_GAME_OF_LIFE:
12884         case EL_BIOMAZE:
12885           Life(x, y);
12886           break;
12887
12888         case EL_EXIT_CLOSED:
12889           CheckExit(x, y);
12890           break;
12891
12892         case EL_EM_EXIT_CLOSED:
12893           CheckExitEM(x, y);
12894           break;
12895
12896         case EL_STEEL_EXIT_CLOSED:
12897           CheckExitSteel(x, y);
12898           break;
12899
12900         case EL_EM_STEEL_EXIT_CLOSED:
12901           CheckExitSteelEM(x, y);
12902           break;
12903
12904         case EL_SP_EXIT_CLOSED:
12905           CheckExitSP(x, y);
12906           break;
12907
12908         case EL_EXPANDABLE_WALL_GROWING:
12909         case EL_EXPANDABLE_STEELWALL_GROWING:
12910           MauerWaechst(x, y);
12911           break;
12912
12913         case EL_EXPANDABLE_WALL:
12914         case EL_EXPANDABLE_WALL_HORIZONTAL:
12915         case EL_EXPANDABLE_WALL_VERTICAL:
12916         case EL_EXPANDABLE_WALL_ANY:
12917         case EL_BD_EXPANDABLE_WALL:
12918           MauerAbleger(x, y);
12919           break;
12920
12921         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12922         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12923         case EL_EXPANDABLE_STEELWALL_ANY:
12924           MauerAblegerStahl(x, y);
12925           break;
12926
12927         case EL_FLAMES:
12928           CheckForDragon(x, y);
12929           break;
12930
12931         case EL_EXPLOSION:
12932           break;
12933
12934         case EL_ELEMENT_SNAPPING:
12935         case EL_DIAGONAL_SHRINKING:
12936         case EL_DIAGONAL_GROWING:
12937         {
12938           graphic =
12939             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12940
12941           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12942           break;
12943         }
12944
12945         default:
12946           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12947             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12948           break;
12949       }
12950     }
12951
12952 #else   // ---------------------------------------------------------------------
12953
12954     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12955     {
12956       StartMoving(x, y);
12957
12958       element = Feld[x][y];
12959       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12960
12961       if (IS_ANIMATED(graphic) &&
12962           !IS_MOVING(x, y) &&
12963           !Stop[x][y])
12964         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12965
12966       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12967         TEST_DrawTwinkleOnField(x, y);
12968     }
12969     else if ((element == EL_ACID ||
12970               element == EL_EXIT_OPEN ||
12971               element == EL_EM_EXIT_OPEN ||
12972               element == EL_SP_EXIT_OPEN ||
12973               element == EL_STEEL_EXIT_OPEN ||
12974               element == EL_EM_STEEL_EXIT_OPEN ||
12975               element == EL_SP_TERMINAL ||
12976               element == EL_SP_TERMINAL_ACTIVE ||
12977               element == EL_EXTRA_TIME ||
12978               element == EL_SHIELD_NORMAL ||
12979               element == EL_SHIELD_DEADLY) &&
12980              IS_ANIMATED(graphic))
12981       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12982     else if (IS_MOVING(x, y))
12983       ContinueMoving(x, y);
12984     else if (IS_ACTIVE_BOMB(element))
12985       CheckDynamite(x, y);
12986     else if (element == EL_AMOEBA_GROWING)
12987       AmoebeWaechst(x, y);
12988     else if (element == EL_AMOEBA_SHRINKING)
12989       AmoebaDisappearing(x, y);
12990
12991 #if !USE_NEW_AMOEBA_CODE
12992     else if (IS_AMOEBALIVE(element))
12993       AmoebeAbleger(x, y);
12994 #endif
12995
12996     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12997       Life(x, y);
12998     else if (element == EL_EXIT_CLOSED)
12999       CheckExit(x, y);
13000     else if (element == EL_EM_EXIT_CLOSED)
13001       CheckExitEM(x, y);
13002     else if (element == EL_STEEL_EXIT_CLOSED)
13003       CheckExitSteel(x, y);
13004     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13005       CheckExitSteelEM(x, y);
13006     else if (element == EL_SP_EXIT_CLOSED)
13007       CheckExitSP(x, y);
13008     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13009              element == EL_EXPANDABLE_STEELWALL_GROWING)
13010       MauerWaechst(x, y);
13011     else if (element == EL_EXPANDABLE_WALL ||
13012              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13013              element == EL_EXPANDABLE_WALL_VERTICAL ||
13014              element == EL_EXPANDABLE_WALL_ANY ||
13015              element == EL_BD_EXPANDABLE_WALL)
13016       MauerAbleger(x, y);
13017     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13018              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13019              element == EL_EXPANDABLE_STEELWALL_ANY)
13020       MauerAblegerStahl(x, y);
13021     else if (element == EL_FLAMES)
13022       CheckForDragon(x, y);
13023     else if (element == EL_EXPLOSION)
13024       ; /* drawing of correct explosion animation is handled separately */
13025     else if (element == EL_ELEMENT_SNAPPING ||
13026              element == EL_DIAGONAL_SHRINKING ||
13027              element == EL_DIAGONAL_GROWING)
13028     {
13029       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13030
13031       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13032     }
13033     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13034       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13035
13036 #endif  // ---------------------------------------------------------------------
13037
13038     if (IS_BELT_ACTIVE(element))
13039       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13040
13041     if (game.magic_wall_active)
13042     {
13043       int jx = local_player->jx, jy = local_player->jy;
13044
13045       /* play the element sound at the position nearest to the player */
13046       if ((element == EL_MAGIC_WALL_FULL ||
13047            element == EL_MAGIC_WALL_ACTIVE ||
13048            element == EL_MAGIC_WALL_EMPTYING ||
13049            element == EL_BD_MAGIC_WALL_FULL ||
13050            element == EL_BD_MAGIC_WALL_ACTIVE ||
13051            element == EL_BD_MAGIC_WALL_EMPTYING ||
13052            element == EL_DC_MAGIC_WALL_FULL ||
13053            element == EL_DC_MAGIC_WALL_ACTIVE ||
13054            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13055           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13056       {
13057         magic_wall_x = x;
13058         magic_wall_y = y;
13059       }
13060     }
13061   }
13062
13063 #if 0
13064   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13065 #endif
13066
13067 #if USE_NEW_AMOEBA_CODE
13068   /* new experimental amoeba growth stuff */
13069   if (!(FrameCounter % 8))
13070   {
13071     static unsigned long random = 1684108901;
13072
13073     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13074     {
13075       x = RND(lev_fieldx);
13076       y = RND(lev_fieldy);
13077       element = Feld[x][y];
13078
13079       if (!IS_PLAYER(x,y) &&
13080           (element == EL_EMPTY ||
13081            CAN_GROW_INTO(element) ||
13082            element == EL_QUICKSAND_EMPTY ||
13083            element == EL_QUICKSAND_FAST_EMPTY ||
13084            element == EL_ACID_SPLASH_LEFT ||
13085            element == EL_ACID_SPLASH_RIGHT))
13086       {
13087         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13088             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13089             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13090             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13091           Feld[x][y] = EL_AMOEBA_DROP;
13092       }
13093
13094       random = random * 129 + 1;
13095     }
13096   }
13097 #endif
13098
13099 #if 0
13100   if (game.explosions_delayed)
13101 #endif
13102   {
13103     game.explosions_delayed = FALSE;
13104
13105     SCAN_PLAYFIELD(x, y)
13106     {
13107       element = Feld[x][y];
13108
13109       if (ExplodeField[x][y])
13110         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13111       else if (element == EL_EXPLOSION)
13112         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13113
13114       ExplodeField[x][y] = EX_TYPE_NONE;
13115     }
13116
13117     game.explosions_delayed = TRUE;
13118   }
13119
13120   if (game.magic_wall_active)
13121   {
13122     if (!(game.magic_wall_time_left % 4))
13123     {
13124       int element = Feld[magic_wall_x][magic_wall_y];
13125
13126       if (element == EL_BD_MAGIC_WALL_FULL ||
13127           element == EL_BD_MAGIC_WALL_ACTIVE ||
13128           element == EL_BD_MAGIC_WALL_EMPTYING)
13129         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13130       else if (element == EL_DC_MAGIC_WALL_FULL ||
13131                element == EL_DC_MAGIC_WALL_ACTIVE ||
13132                element == EL_DC_MAGIC_WALL_EMPTYING)
13133         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13134       else
13135         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13136     }
13137
13138     if (game.magic_wall_time_left > 0)
13139     {
13140       game.magic_wall_time_left--;
13141
13142       if (!game.magic_wall_time_left)
13143       {
13144         SCAN_PLAYFIELD(x, y)
13145         {
13146           element = Feld[x][y];
13147
13148           if (element == EL_MAGIC_WALL_ACTIVE ||
13149               element == EL_MAGIC_WALL_FULL)
13150           {
13151             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13152             TEST_DrawLevelField(x, y);
13153           }
13154           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13155                    element == EL_BD_MAGIC_WALL_FULL)
13156           {
13157             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13158             TEST_DrawLevelField(x, y);
13159           }
13160           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13161                    element == EL_DC_MAGIC_WALL_FULL)
13162           {
13163             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13164             TEST_DrawLevelField(x, y);
13165           }
13166         }
13167
13168         game.magic_wall_active = FALSE;
13169       }
13170     }
13171   }
13172
13173   if (game.light_time_left > 0)
13174   {
13175     game.light_time_left--;
13176
13177     if (game.light_time_left == 0)
13178       RedrawAllLightSwitchesAndInvisibleElements();
13179   }
13180
13181   if (game.timegate_time_left > 0)
13182   {
13183     game.timegate_time_left--;
13184
13185     if (game.timegate_time_left == 0)
13186       CloseAllOpenTimegates();
13187   }
13188
13189   if (game.lenses_time_left > 0)
13190   {
13191     game.lenses_time_left--;
13192
13193     if (game.lenses_time_left == 0)
13194       RedrawAllInvisibleElementsForLenses();
13195   }
13196
13197   if (game.magnify_time_left > 0)
13198   {
13199     game.magnify_time_left--;
13200
13201     if (game.magnify_time_left == 0)
13202       RedrawAllInvisibleElementsForMagnifier();
13203   }
13204
13205   for (i = 0; i < MAX_PLAYERS; i++)
13206   {
13207     struct PlayerInfo *player = &stored_player[i];
13208
13209     if (SHIELD_ON(player))
13210     {
13211       if (player->shield_deadly_time_left)
13212         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13213       else if (player->shield_normal_time_left)
13214         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13215     }
13216   }
13217
13218 #if USE_DELAYED_GFX_REDRAW
13219   SCAN_PLAYFIELD(x, y)
13220   {
13221 #if 1
13222     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13223 #else
13224     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13225         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13226 #endif
13227     {
13228       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13229          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13230
13231       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13232         DrawLevelField(x, y);
13233
13234       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13235         DrawLevelFieldCrumbled(x, y);
13236
13237       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13238         DrawLevelFieldCrumbledNeighbours(x, y);
13239
13240       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13241         DrawTwinkleOnField(x, y);
13242     }
13243
13244     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13245   }
13246 #endif
13247
13248   CheckLevelTime();
13249
13250   DrawAllPlayers();
13251   PlayAllPlayersSound();
13252
13253   if (options.debug)                    /* calculate frames per second */
13254   {
13255     static unsigned long fps_counter = 0;
13256     static int fps_frames = 0;
13257     unsigned long fps_delay_ms = Counter() - fps_counter;
13258
13259     fps_frames++;
13260
13261     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13262     {
13263       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13264
13265       fps_frames = 0;
13266       fps_counter = Counter();
13267     }
13268
13269     redraw_mask |= REDRAW_FPS;
13270   }
13271
13272   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13273
13274   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13275   {
13276     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13277
13278     local_player->show_envelope = 0;
13279   }
13280
13281 #if 0
13282   debug_print_timestamp(0, "stop main loop profiling ");
13283   printf("----------------------------------------------------------\n");
13284 #endif
13285
13286   /* use random number generator in every frame to make it less predictable */
13287   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13288     RND(1);
13289 }
13290
13291 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13292 {
13293   int min_x = x, min_y = y, max_x = x, max_y = y;
13294   int i;
13295
13296   for (i = 0; i < MAX_PLAYERS; i++)
13297   {
13298     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13299
13300     if (!stored_player[i].active || &stored_player[i] == player)
13301       continue;
13302
13303     min_x = MIN(min_x, jx);
13304     min_y = MIN(min_y, jy);
13305     max_x = MAX(max_x, jx);
13306     max_y = MAX(max_y, jy);
13307   }
13308
13309   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13310 }
13311
13312 static boolean AllPlayersInVisibleScreen()
13313 {
13314   int i;
13315
13316   for (i = 0; i < MAX_PLAYERS; i++)
13317   {
13318     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13319
13320     if (!stored_player[i].active)
13321       continue;
13322
13323     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13324       return FALSE;
13325   }
13326
13327   return TRUE;
13328 }
13329
13330 void ScrollLevel(int dx, int dy)
13331 {
13332 #if 0
13333   /* (directly solved in BlitBitmap() now) */
13334   static Bitmap *bitmap_db_field2 = NULL;
13335   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13336   int x, y;
13337 #else
13338   int x, y;
13339 #endif
13340
13341 #if 0
13342   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13343   /* only horizontal XOR vertical scroll direction allowed */
13344   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13345     return;
13346 #endif
13347
13348 #if 0
13349   /* (directly solved in BlitBitmap() now) */
13350   if (bitmap_db_field2 == NULL)
13351     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13352
13353   /* needed when blitting directly to same bitmap -- should not be needed with
13354      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13355   BlitBitmap(drawto_field, bitmap_db_field2,
13356              FX + TILEX * (dx == -1) - softscroll_offset,
13357              FY + TILEY * (dy == -1) - softscroll_offset,
13358              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13359              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13360              FX + TILEX * (dx == 1) - softscroll_offset,
13361              FY + TILEY * (dy == 1) - softscroll_offset);
13362   BlitBitmap(bitmap_db_field2, drawto_field,
13363              FX + TILEX * (dx == 1) - softscroll_offset,
13364              FY + TILEY * (dy == 1) - softscroll_offset,
13365              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13366              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13367              FX + TILEX * (dx == 1) - softscroll_offset,
13368              FY + TILEY * (dy == 1) - softscroll_offset);
13369
13370 #else
13371
13372 #if 0
13373   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13374   int xsize = (BX2 - BX1 + 1);
13375   int ysize = (BY2 - BY1 + 1);
13376   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13377   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13378   int step  = (start < end ? +1 : -1);
13379
13380   for (i = start; i != end; i += step)
13381   {
13382     BlitBitmap(drawto_field, drawto_field,
13383                FX + TILEX * (dx != 0 ? i + step : 0),
13384                FY + TILEY * (dy != 0 ? i + step : 0),
13385                TILEX * (dx != 0 ? 1 : xsize),
13386                TILEY * (dy != 0 ? 1 : ysize),
13387                FX + TILEX * (dx != 0 ? i : 0),
13388                FY + TILEY * (dy != 0 ? i : 0));
13389   }
13390
13391 #else
13392
13393 #if NEW_TILESIZE
13394 #if NEW_SCROLL
13395   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13396 #else
13397   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13398 #endif
13399 #else
13400 #if NEW_SCROLL
13401   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13402 #else
13403   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13404 #endif
13405 #endif
13406
13407 #if NEW_TILESIZE
13408   BlitBitmap(drawto_field, drawto_field,
13409              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13410              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13411              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13412              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13413              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13414              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13415 #else
13416   BlitBitmap(drawto_field, drawto_field,
13417              FX + TILEX * (dx == -1) - softscroll_offset,
13418              FY + TILEY * (dy == -1) - softscroll_offset,
13419              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13420              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13421              FX + TILEX * (dx == 1) - softscroll_offset,
13422              FY + TILEY * (dy == 1) - softscroll_offset);
13423 #endif
13424
13425 #endif
13426 #endif
13427
13428   if (dx != 0)
13429   {
13430     x = (dx == 1 ? BX1 : BX2);
13431     for (y = BY1; y <= BY2; y++)
13432       DrawScreenField(x, y);
13433   }
13434
13435   if (dy != 0)
13436   {
13437     y = (dy == 1 ? BY1 : BY2);
13438     for (x = BX1; x <= BX2; x++)
13439       DrawScreenField(x, y);
13440   }
13441
13442   redraw_mask |= REDRAW_FIELD;
13443 }
13444
13445 static boolean canFallDown(struct PlayerInfo *player)
13446 {
13447   int jx = player->jx, jy = player->jy;
13448
13449   return (IN_LEV_FIELD(jx, jy + 1) &&
13450           (IS_FREE(jx, jy + 1) ||
13451            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13452           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13453           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13454 }
13455
13456 static boolean canPassField(int x, int y, int move_dir)
13457 {
13458   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13459   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13460   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13461   int nextx = x + dx;
13462   int nexty = y + dy;
13463   int element = Feld[x][y];
13464
13465   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13466           !CAN_MOVE(element) &&
13467           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13468           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13469           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13470 }
13471
13472 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13473 {
13474   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13475   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13476   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13477   int newx = x + dx;
13478   int newy = y + dy;
13479
13480   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13481           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13482           (IS_DIGGABLE(Feld[newx][newy]) ||
13483            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13484            canPassField(newx, newy, move_dir)));
13485 }
13486
13487 static void CheckGravityMovement(struct PlayerInfo *player)
13488 {
13489 #if USE_PLAYER_GRAVITY
13490   if (player->gravity && !player->programmed_action)
13491 #else
13492   if (game.gravity && !player->programmed_action)
13493 #endif
13494   {
13495     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13496     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13497     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13498     int jx = player->jx, jy = player->jy;
13499     boolean player_is_moving_to_valid_field =
13500       (!player_is_snapping &&
13501        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13502         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13503     boolean player_can_fall_down = canFallDown(player);
13504
13505     if (player_can_fall_down &&
13506         !player_is_moving_to_valid_field)
13507       player->programmed_action = MV_DOWN;
13508   }
13509 }
13510
13511 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13512 {
13513   return CheckGravityMovement(player);
13514
13515 #if USE_PLAYER_GRAVITY
13516   if (player->gravity && !player->programmed_action)
13517 #else
13518   if (game.gravity && !player->programmed_action)
13519 #endif
13520   {
13521     int jx = player->jx, jy = player->jy;
13522     boolean field_under_player_is_free =
13523       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13524     boolean player_is_standing_on_valid_field =
13525       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13526        (IS_WALKABLE(Feld[jx][jy]) &&
13527         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13528
13529     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13530       player->programmed_action = MV_DOWN;
13531   }
13532 }
13533
13534 /*
13535   MovePlayerOneStep()
13536   -----------------------------------------------------------------------------
13537   dx, dy:               direction (non-diagonal) to try to move the player to
13538   real_dx, real_dy:     direction as read from input device (can be diagonal)
13539 */
13540
13541 boolean MovePlayerOneStep(struct PlayerInfo *player,
13542                           int dx, int dy, int real_dx, int real_dy)
13543 {
13544   int jx = player->jx, jy = player->jy;
13545   int new_jx = jx + dx, new_jy = jy + dy;
13546 #if !USE_FIXED_DONT_RUN_INTO
13547   int element;
13548 #endif
13549   int can_move;
13550   boolean player_can_move = !player->cannot_move;
13551
13552   if (!player->active || (!dx && !dy))
13553     return MP_NO_ACTION;
13554
13555   player->MovDir = (dx < 0 ? MV_LEFT :
13556                     dx > 0 ? MV_RIGHT :
13557                     dy < 0 ? MV_UP :
13558                     dy > 0 ? MV_DOWN :  MV_NONE);
13559
13560   if (!IN_LEV_FIELD(new_jx, new_jy))
13561     return MP_NO_ACTION;
13562
13563   if (!player_can_move)
13564   {
13565     if (player->MovPos == 0)
13566     {
13567       player->is_moving = FALSE;
13568       player->is_digging = FALSE;
13569       player->is_collecting = FALSE;
13570       player->is_snapping = FALSE;
13571       player->is_pushing = FALSE;
13572     }
13573   }
13574
13575 #if 1
13576   if (!options.network && game.centered_player_nr == -1 &&
13577       !AllPlayersInSight(player, new_jx, new_jy))
13578     return MP_NO_ACTION;
13579 #else
13580   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13581     return MP_NO_ACTION;
13582 #endif
13583
13584 #if !USE_FIXED_DONT_RUN_INTO
13585   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13586
13587   /* (moved to DigField()) */
13588   if (player_can_move && DONT_RUN_INTO(element))
13589   {
13590     if (element == EL_ACID && dx == 0 && dy == 1)
13591     {
13592       SplashAcid(new_jx, new_jy);
13593       Feld[jx][jy] = EL_PLAYER_1;
13594       InitMovingField(jx, jy, MV_DOWN);
13595       Store[jx][jy] = EL_ACID;
13596       ContinueMoving(jx, jy);
13597       BuryPlayer(player);
13598     }
13599     else
13600       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13601
13602     return MP_MOVING;
13603   }
13604 #endif
13605
13606   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13607   if (can_move != MP_MOVING)
13608     return can_move;
13609
13610   /* check if DigField() has caused relocation of the player */
13611   if (player->jx != jx || player->jy != jy)
13612     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13613
13614   StorePlayer[jx][jy] = 0;
13615   player->last_jx = jx;
13616   player->last_jy = jy;
13617   player->jx = new_jx;
13618   player->jy = new_jy;
13619   StorePlayer[new_jx][new_jy] = player->element_nr;
13620
13621   if (player->move_delay_value_next != -1)
13622   {
13623     player->move_delay_value = player->move_delay_value_next;
13624     player->move_delay_value_next = -1;
13625   }
13626
13627   player->MovPos =
13628     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13629
13630   player->step_counter++;
13631
13632   PlayerVisit[jx][jy] = FrameCounter;
13633
13634 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13635   player->is_moving = TRUE;
13636 #endif
13637
13638 #if 1
13639   /* should better be called in MovePlayer(), but this breaks some tapes */
13640   ScrollPlayer(player, SCROLL_INIT);
13641 #endif
13642
13643   return MP_MOVING;
13644 }
13645
13646 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13647 {
13648   int jx = player->jx, jy = player->jy;
13649   int old_jx = jx, old_jy = jy;
13650   int moved = MP_NO_ACTION;
13651
13652   if (!player->active)
13653     return FALSE;
13654
13655   if (!dx && !dy)
13656   {
13657     if (player->MovPos == 0)
13658     {
13659       player->is_moving = FALSE;
13660       player->is_digging = FALSE;
13661       player->is_collecting = FALSE;
13662       player->is_snapping = FALSE;
13663       player->is_pushing = FALSE;
13664     }
13665
13666     return FALSE;
13667   }
13668
13669   if (player->move_delay > 0)
13670     return FALSE;
13671
13672   player->move_delay = -1;              /* set to "uninitialized" value */
13673
13674   /* store if player is automatically moved to next field */
13675   player->is_auto_moving = (player->programmed_action != MV_NONE);
13676
13677   /* remove the last programmed player action */
13678   player->programmed_action = 0;
13679
13680   if (player->MovPos)
13681   {
13682     /* should only happen if pre-1.2 tape recordings are played */
13683     /* this is only for backward compatibility */
13684
13685     int original_move_delay_value = player->move_delay_value;
13686
13687 #if DEBUG
13688     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13689            tape.counter);
13690 #endif
13691
13692     /* scroll remaining steps with finest movement resolution */
13693     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13694
13695     while (player->MovPos)
13696     {
13697       ScrollPlayer(player, SCROLL_GO_ON);
13698       ScrollScreen(NULL, SCROLL_GO_ON);
13699
13700       AdvanceFrameAndPlayerCounters(player->index_nr);
13701
13702       DrawAllPlayers();
13703       BackToFront();
13704     }
13705
13706     player->move_delay_value = original_move_delay_value;
13707   }
13708
13709   player->is_active = FALSE;
13710
13711   if (player->last_move_dir & MV_HORIZONTAL)
13712   {
13713     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13714       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13715   }
13716   else
13717   {
13718     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13719       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13720   }
13721
13722 #if USE_FIXED_BORDER_RUNNING_GFX
13723   if (!moved && !player->is_active)
13724   {
13725     player->is_moving = FALSE;
13726     player->is_digging = FALSE;
13727     player->is_collecting = FALSE;
13728     player->is_snapping = FALSE;
13729     player->is_pushing = FALSE;
13730   }
13731 #endif
13732
13733   jx = player->jx;
13734   jy = player->jy;
13735
13736 #if 1
13737   if (moved & MP_MOVING && !ScreenMovPos &&
13738       (player->index_nr == game.centered_player_nr ||
13739        game.centered_player_nr == -1))
13740 #else
13741   if (moved & MP_MOVING && !ScreenMovPos &&
13742       (player == local_player || !options.network))
13743 #endif
13744   {
13745     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13746     int offset = game.scroll_delay_value;
13747
13748     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13749     {
13750       /* actual player has left the screen -- scroll in that direction */
13751       if (jx != old_jx)         /* player has moved horizontally */
13752         scroll_x += (jx - old_jx);
13753       else                      /* player has moved vertically */
13754         scroll_y += (jy - old_jy);
13755     }
13756     else
13757     {
13758       if (jx != old_jx)         /* player has moved horizontally */
13759       {
13760         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13761             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13762           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13763
13764         /* don't scroll over playfield boundaries */
13765         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13766           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13767
13768         /* don't scroll more than one field at a time */
13769         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13770
13771         /* don't scroll against the player's moving direction */
13772         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13773             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13774           scroll_x = old_scroll_x;
13775       }
13776       else                      /* player has moved vertically */
13777       {
13778         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13779             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13780           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13781
13782         /* don't scroll over playfield boundaries */
13783         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13784           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13785
13786         /* don't scroll more than one field at a time */
13787         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13788
13789         /* don't scroll against the player's moving direction */
13790         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13791             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13792           scroll_y = old_scroll_y;
13793       }
13794     }
13795
13796     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13797     {
13798 #if 1
13799       if (!options.network && game.centered_player_nr == -1 &&
13800           !AllPlayersInVisibleScreen())
13801       {
13802         scroll_x = old_scroll_x;
13803         scroll_y = old_scroll_y;
13804       }
13805       else
13806 #else
13807       if (!options.network && !AllPlayersInVisibleScreen())
13808       {
13809         scroll_x = old_scroll_x;
13810         scroll_y = old_scroll_y;
13811       }
13812       else
13813 #endif
13814       {
13815         ScrollScreen(player, SCROLL_INIT);
13816         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13817       }
13818     }
13819   }
13820
13821   player->StepFrame = 0;
13822
13823   if (moved & MP_MOVING)
13824   {
13825     if (old_jx != jx && old_jy == jy)
13826       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13827     else if (old_jx == jx && old_jy != jy)
13828       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13829
13830     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13831
13832     player->last_move_dir = player->MovDir;
13833     player->is_moving = TRUE;
13834     player->is_snapping = FALSE;
13835     player->is_switching = FALSE;
13836     player->is_dropping = FALSE;
13837     player->is_dropping_pressed = FALSE;
13838     player->drop_pressed_delay = 0;
13839
13840 #if 0
13841     /* should better be called here than above, but this breaks some tapes */
13842     ScrollPlayer(player, SCROLL_INIT);
13843 #endif
13844   }
13845   else
13846   {
13847     CheckGravityMovementWhenNotMoving(player);
13848
13849     player->is_moving = FALSE;
13850
13851     /* at this point, the player is allowed to move, but cannot move right now
13852        (e.g. because of something blocking the way) -- ensure that the player
13853        is also allowed to move in the next frame (in old versions before 3.1.1,
13854        the player was forced to wait again for eight frames before next try) */
13855
13856     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13857       player->move_delay = 0;   /* allow direct movement in the next frame */
13858   }
13859
13860   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13861     player->move_delay = player->move_delay_value;
13862
13863   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13864   {
13865     TestIfPlayerTouchesBadThing(jx, jy);
13866     TestIfPlayerTouchesCustomElement(jx, jy);
13867   }
13868
13869   if (!player->active)
13870     RemovePlayer(player);
13871
13872   return moved;
13873 }
13874
13875 void ScrollPlayer(struct PlayerInfo *player, int mode)
13876 {
13877   int jx = player->jx, jy = player->jy;
13878   int last_jx = player->last_jx, last_jy = player->last_jy;
13879   int move_stepsize = TILEX / player->move_delay_value;
13880
13881 #if USE_NEW_PLAYER_SPEED
13882   if (!player->active)
13883     return;
13884
13885   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13886     return;
13887 #else
13888   if (!player->active || player->MovPos == 0)
13889     return;
13890 #endif
13891
13892   if (mode == SCROLL_INIT)
13893   {
13894     player->actual_frame_counter = FrameCounter;
13895     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13896
13897     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13898         Feld[last_jx][last_jy] == EL_EMPTY)
13899     {
13900       int last_field_block_delay = 0;   /* start with no blocking at all */
13901       int block_delay_adjustment = player->block_delay_adjustment;
13902
13903       /* if player blocks last field, add delay for exactly one move */
13904       if (player->block_last_field)
13905       {
13906         last_field_block_delay += player->move_delay_value;
13907
13908         /* when blocking enabled, prevent moving up despite gravity */
13909 #if USE_PLAYER_GRAVITY
13910         if (player->gravity && player->MovDir == MV_UP)
13911           block_delay_adjustment = -1;
13912 #else
13913         if (game.gravity && player->MovDir == MV_UP)
13914           block_delay_adjustment = -1;
13915 #endif
13916       }
13917
13918       /* add block delay adjustment (also possible when not blocking) */
13919       last_field_block_delay += block_delay_adjustment;
13920
13921       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13922       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13923     }
13924
13925 #if USE_NEW_PLAYER_SPEED
13926     if (player->MovPos != 0)    /* player has not yet reached destination */
13927       return;
13928 #else
13929     return;
13930 #endif
13931   }
13932   else if (!FrameReached(&player->actual_frame_counter, 1))
13933     return;
13934
13935 #if USE_NEW_PLAYER_SPEED
13936   if (player->MovPos != 0)
13937   {
13938     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13939     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13940
13941     /* before DrawPlayer() to draw correct player graphic for this case */
13942     if (player->MovPos == 0)
13943       CheckGravityMovement(player);
13944   }
13945 #else
13946   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13947   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13948
13949   /* before DrawPlayer() to draw correct player graphic for this case */
13950   if (player->MovPos == 0)
13951     CheckGravityMovement(player);
13952 #endif
13953
13954   if (player->MovPos == 0)      /* player reached destination field */
13955   {
13956     if (player->move_delay_reset_counter > 0)
13957     {
13958       player->move_delay_reset_counter--;
13959
13960       if (player->move_delay_reset_counter == 0)
13961       {
13962         /* continue with normal speed after quickly moving through gate */
13963         HALVE_PLAYER_SPEED(player);
13964
13965         /* be able to make the next move without delay */
13966         player->move_delay = 0;
13967       }
13968     }
13969
13970     player->last_jx = jx;
13971     player->last_jy = jy;
13972
13973     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13974         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13975 #if 1
13976         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13977 #endif
13978         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13979         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13980 #if 1
13981         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13982 #endif
13983         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13984         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13985     {
13986       DrawPlayer(player);       /* needed here only to cleanup last field */
13987       RemovePlayer(player);
13988
13989       if (local_player->friends_still_needed == 0 ||
13990           IS_SP_ELEMENT(Feld[jx][jy]))
13991         PlayerWins(player);
13992     }
13993
13994     /* this breaks one level: "machine", level 000 */
13995     {
13996       int move_direction = player->MovDir;
13997       int enter_side = MV_DIR_OPPOSITE(move_direction);
13998       int leave_side = move_direction;
13999       int old_jx = last_jx;
14000       int old_jy = last_jy;
14001       int old_element = Feld[old_jx][old_jy];
14002       int new_element = Feld[jx][jy];
14003
14004       if (IS_CUSTOM_ELEMENT(old_element))
14005         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14006                                    CE_LEFT_BY_PLAYER,
14007                                    player->index_bit, leave_side);
14008
14009       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14010                                           CE_PLAYER_LEAVES_X,
14011                                           player->index_bit, leave_side);
14012
14013       if (IS_CUSTOM_ELEMENT(new_element))
14014         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14015                                    player->index_bit, enter_side);
14016
14017       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14018                                           CE_PLAYER_ENTERS_X,
14019                                           player->index_bit, enter_side);
14020
14021 #if USE_FIX_CE_ACTION_WITH_PLAYER
14022       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14023                                         CE_MOVE_OF_X, move_direction);
14024 #else
14025       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14026                                         CE_MOVE_OF_X, move_direction);
14027 #endif
14028     }
14029
14030     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14031     {
14032       TestIfPlayerTouchesBadThing(jx, jy);
14033       TestIfPlayerTouchesCustomElement(jx, jy);
14034
14035       /* needed because pushed element has not yet reached its destination,
14036          so it would trigger a change event at its previous field location */
14037       if (!player->is_pushing)
14038         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14039
14040       if (!player->active)
14041         RemovePlayer(player);
14042     }
14043
14044     if (!local_player->LevelSolved && level.use_step_counter)
14045     {
14046       int i;
14047
14048       TimePlayed++;
14049
14050       if (TimeLeft > 0)
14051       {
14052         TimeLeft--;
14053
14054         if (TimeLeft <= 10 && setup.time_limit)
14055           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14056
14057 #if 1
14058         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14059
14060         DisplayGameControlValues();
14061 #else
14062         DrawGameValue_Time(TimeLeft);
14063 #endif
14064
14065         if (!TimeLeft && setup.time_limit)
14066           for (i = 0; i < MAX_PLAYERS; i++)
14067             KillPlayer(&stored_player[i]);
14068       }
14069 #if 1
14070       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14071       {
14072         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14073
14074         DisplayGameControlValues();
14075       }
14076 #else
14077       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14078         DrawGameValue_Time(TimePlayed);
14079 #endif
14080     }
14081
14082     if (tape.single_step && tape.recording && !tape.pausing &&
14083         !player->programmed_action)
14084       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14085   }
14086 }
14087
14088 void ScrollScreen(struct PlayerInfo *player, int mode)
14089 {
14090   static unsigned long screen_frame_counter = 0;
14091
14092   if (mode == SCROLL_INIT)
14093   {
14094     /* set scrolling step size according to actual player's moving speed */
14095     ScrollStepSize = TILEX / player->move_delay_value;
14096
14097     screen_frame_counter = FrameCounter;
14098     ScreenMovDir = player->MovDir;
14099     ScreenMovPos = player->MovPos;
14100     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14101     return;
14102   }
14103   else if (!FrameReached(&screen_frame_counter, 1))
14104     return;
14105
14106   if (ScreenMovPos)
14107   {
14108     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14109     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14110     redraw_mask |= REDRAW_FIELD;
14111   }
14112   else
14113     ScreenMovDir = MV_NONE;
14114 }
14115
14116 void TestIfPlayerTouchesCustomElement(int x, int y)
14117 {
14118   static int xy[4][2] =
14119   {
14120     { 0, -1 },
14121     { -1, 0 },
14122     { +1, 0 },
14123     { 0, +1 }
14124   };
14125   static int trigger_sides[4][2] =
14126   {
14127     /* center side       border side */
14128     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14129     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14130     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14131     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14132   };
14133   static int touch_dir[4] =
14134   {
14135     MV_LEFT | MV_RIGHT,
14136     MV_UP   | MV_DOWN,
14137     MV_UP   | MV_DOWN,
14138     MV_LEFT | MV_RIGHT
14139   };
14140   int center_element = Feld[x][y];      /* should always be non-moving! */
14141   int i;
14142
14143   for (i = 0; i < NUM_DIRECTIONS; i++)
14144   {
14145     int xx = x + xy[i][0];
14146     int yy = y + xy[i][1];
14147     int center_side = trigger_sides[i][0];
14148     int border_side = trigger_sides[i][1];
14149     int border_element;
14150
14151     if (!IN_LEV_FIELD(xx, yy))
14152       continue;
14153
14154     if (IS_PLAYER(x, y))                /* player found at center element */
14155     {
14156       struct PlayerInfo *player = PLAYERINFO(x, y);
14157
14158       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14159         border_element = Feld[xx][yy];          /* may be moving! */
14160       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14161         border_element = Feld[xx][yy];
14162       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14163         border_element = MovingOrBlocked2Element(xx, yy);
14164       else
14165         continue;               /* center and border element do not touch */
14166
14167       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14168                                  player->index_bit, border_side);
14169       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14170                                           CE_PLAYER_TOUCHES_X,
14171                                           player->index_bit, border_side);
14172
14173 #if USE_FIX_CE_ACTION_WITH_PLAYER
14174       {
14175         /* use player element that is initially defined in the level playfield,
14176            not the player element that corresponds to the runtime player number
14177            (example: a level that contains EL_PLAYER_3 as the only player would
14178            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14179         int player_element = PLAYERINFO(x, y)->initial_element;
14180
14181         CheckElementChangeBySide(xx, yy, border_element, player_element,
14182                                  CE_TOUCHING_X, border_side);
14183       }
14184 #endif
14185     }
14186     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14187     {
14188       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14189
14190       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14191       {
14192         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14193           continue;             /* center and border element do not touch */
14194       }
14195
14196       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14197                                  player->index_bit, center_side);
14198       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14199                                           CE_PLAYER_TOUCHES_X,
14200                                           player->index_bit, center_side);
14201
14202 #if USE_FIX_CE_ACTION_WITH_PLAYER
14203       {
14204         /* use player element that is initially defined in the level playfield,
14205            not the player element that corresponds to the runtime player number
14206            (example: a level that contains EL_PLAYER_3 as the only player would
14207            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14208         int player_element = PLAYERINFO(xx, yy)->initial_element;
14209
14210         CheckElementChangeBySide(x, y, center_element, player_element,
14211                                  CE_TOUCHING_X, center_side);
14212       }
14213 #endif
14214
14215       break;
14216     }
14217   }
14218 }
14219
14220 #if USE_ELEMENT_TOUCHING_BUGFIX
14221
14222 void TestIfElementTouchesCustomElement(int x, int y)
14223 {
14224   static int xy[4][2] =
14225   {
14226     { 0, -1 },
14227     { -1, 0 },
14228     { +1, 0 },
14229     { 0, +1 }
14230   };
14231   static int trigger_sides[4][2] =
14232   {
14233     /* center side      border side */
14234     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14235     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14236     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14237     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14238   };
14239   static int touch_dir[4] =
14240   {
14241     MV_LEFT | MV_RIGHT,
14242     MV_UP   | MV_DOWN,
14243     MV_UP   | MV_DOWN,
14244     MV_LEFT | MV_RIGHT
14245   };
14246   boolean change_center_element = FALSE;
14247   int center_element = Feld[x][y];      /* should always be non-moving! */
14248   int border_element_old[NUM_DIRECTIONS];
14249   int i;
14250
14251   for (i = 0; i < NUM_DIRECTIONS; i++)
14252   {
14253     int xx = x + xy[i][0];
14254     int yy = y + xy[i][1];
14255     int border_element;
14256
14257     border_element_old[i] = -1;
14258
14259     if (!IN_LEV_FIELD(xx, yy))
14260       continue;
14261
14262     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14263       border_element = Feld[xx][yy];    /* may be moving! */
14264     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14265       border_element = Feld[xx][yy];
14266     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14267       border_element = MovingOrBlocked2Element(xx, yy);
14268     else
14269       continue;                 /* center and border element do not touch */
14270
14271     border_element_old[i] = border_element;
14272   }
14273
14274   for (i = 0; i < NUM_DIRECTIONS; i++)
14275   {
14276     int xx = x + xy[i][0];
14277     int yy = y + xy[i][1];
14278     int center_side = trigger_sides[i][0];
14279     int border_element = border_element_old[i];
14280
14281     if (border_element == -1)
14282       continue;
14283
14284     /* check for change of border element */
14285     CheckElementChangeBySide(xx, yy, border_element, center_element,
14286                              CE_TOUCHING_X, center_side);
14287
14288     /* (center element cannot be player, so we dont have to check this here) */
14289   }
14290
14291   for (i = 0; i < NUM_DIRECTIONS; i++)
14292   {
14293     int xx = x + xy[i][0];
14294     int yy = y + xy[i][1];
14295     int border_side = trigger_sides[i][1];
14296     int border_element = border_element_old[i];
14297
14298     if (border_element == -1)
14299       continue;
14300
14301     /* check for change of center element (but change it only once) */
14302     if (!change_center_element)
14303       change_center_element =
14304         CheckElementChangeBySide(x, y, center_element, border_element,
14305                                  CE_TOUCHING_X, border_side);
14306
14307 #if USE_FIX_CE_ACTION_WITH_PLAYER
14308     if (IS_PLAYER(xx, yy))
14309     {
14310       /* use player element that is initially defined in the level playfield,
14311          not the player element that corresponds to the runtime player number
14312          (example: a level that contains EL_PLAYER_3 as the only player would
14313          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14314       int player_element = PLAYERINFO(xx, yy)->initial_element;
14315
14316       CheckElementChangeBySide(x, y, center_element, player_element,
14317                                CE_TOUCHING_X, border_side);
14318     }
14319 #endif
14320   }
14321 }
14322
14323 #else
14324
14325 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14326 {
14327   static int xy[4][2] =
14328   {
14329     { 0, -1 },
14330     { -1, 0 },
14331     { +1, 0 },
14332     { 0, +1 }
14333   };
14334   static int trigger_sides[4][2] =
14335   {
14336     /* center side      border side */
14337     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14338     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14339     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14340     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14341   };
14342   static int touch_dir[4] =
14343   {
14344     MV_LEFT | MV_RIGHT,
14345     MV_UP   | MV_DOWN,
14346     MV_UP   | MV_DOWN,
14347     MV_LEFT | MV_RIGHT
14348   };
14349   boolean change_center_element = FALSE;
14350   int center_element = Feld[x][y];      /* should always be non-moving! */
14351   int i;
14352
14353   for (i = 0; i < NUM_DIRECTIONS; i++)
14354   {
14355     int xx = x + xy[i][0];
14356     int yy = y + xy[i][1];
14357     int center_side = trigger_sides[i][0];
14358     int border_side = trigger_sides[i][1];
14359     int border_element;
14360
14361     if (!IN_LEV_FIELD(xx, yy))
14362       continue;
14363
14364     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14365       border_element = Feld[xx][yy];    /* may be moving! */
14366     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14367       border_element = Feld[xx][yy];
14368     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14369       border_element = MovingOrBlocked2Element(xx, yy);
14370     else
14371       continue;                 /* center and border element do not touch */
14372
14373     /* check for change of center element (but change it only once) */
14374     if (!change_center_element)
14375       change_center_element =
14376         CheckElementChangeBySide(x, y, center_element, border_element,
14377                                  CE_TOUCHING_X, border_side);
14378
14379     /* check for change of border element */
14380     CheckElementChangeBySide(xx, yy, border_element, center_element,
14381                              CE_TOUCHING_X, center_side);
14382   }
14383 }
14384
14385 #endif
14386
14387 void TestIfElementHitsCustomElement(int x, int y, int direction)
14388 {
14389   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14390   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14391   int hitx = x + dx, hity = y + dy;
14392   int hitting_element = Feld[x][y];
14393   int touched_element;
14394
14395   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14396     return;
14397
14398   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14399                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14400
14401   if (IN_LEV_FIELD(hitx, hity))
14402   {
14403     int opposite_direction = MV_DIR_OPPOSITE(direction);
14404     int hitting_side = direction;
14405     int touched_side = opposite_direction;
14406     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14407                           MovDir[hitx][hity] != direction ||
14408                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14409
14410     object_hit = TRUE;
14411
14412     if (object_hit)
14413     {
14414       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14415                                CE_HITTING_X, touched_side);
14416
14417       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14418                                CE_HIT_BY_X, hitting_side);
14419
14420       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14421                                CE_HIT_BY_SOMETHING, opposite_direction);
14422
14423 #if USE_FIX_CE_ACTION_WITH_PLAYER
14424       if (IS_PLAYER(hitx, hity))
14425       {
14426         /* use player element that is initially defined in the level playfield,
14427            not the player element that corresponds to the runtime player number
14428            (example: a level that contains EL_PLAYER_3 as the only player would
14429            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14430         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14431
14432         CheckElementChangeBySide(x, y, hitting_element, player_element,
14433                                  CE_HITTING_X, touched_side);
14434       }
14435 #endif
14436     }
14437   }
14438
14439   /* "hitting something" is also true when hitting the playfield border */
14440   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14441                            CE_HITTING_SOMETHING, direction);
14442 }
14443
14444 #if 0
14445 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14446 {
14447   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14448   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14449   int hitx = x + dx, hity = y + dy;
14450   int hitting_element = Feld[x][y];
14451   int touched_element;
14452 #if 0
14453   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14454                         !IS_FREE(hitx, hity) &&
14455                         (!IS_MOVING(hitx, hity) ||
14456                          MovDir[hitx][hity] != direction ||
14457                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14458 #endif
14459
14460   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14461     return;
14462
14463 #if 0
14464   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14465     return;
14466 #endif
14467
14468   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14469                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14470
14471   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14472                            EP_CAN_SMASH_EVERYTHING, direction);
14473
14474   if (IN_LEV_FIELD(hitx, hity))
14475   {
14476     int opposite_direction = MV_DIR_OPPOSITE(direction);
14477     int hitting_side = direction;
14478     int touched_side = opposite_direction;
14479 #if 0
14480     int touched_element = MovingOrBlocked2Element(hitx, hity);
14481 #endif
14482 #if 1
14483     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14484                           MovDir[hitx][hity] != direction ||
14485                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14486
14487     object_hit = TRUE;
14488 #endif
14489
14490     if (object_hit)
14491     {
14492       int i;
14493
14494       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14495                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14496
14497       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14498                                CE_OTHER_IS_SMASHING, touched_side);
14499
14500       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14501                                CE_OTHER_GETS_SMASHED, hitting_side);
14502     }
14503   }
14504 }
14505 #endif
14506
14507 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14508 {
14509   int i, kill_x = -1, kill_y = -1;
14510
14511   int bad_element = -1;
14512   static int test_xy[4][2] =
14513   {
14514     { 0, -1 },
14515     { -1, 0 },
14516     { +1, 0 },
14517     { 0, +1 }
14518   };
14519   static int test_dir[4] =
14520   {
14521     MV_UP,
14522     MV_LEFT,
14523     MV_RIGHT,
14524     MV_DOWN
14525   };
14526
14527   for (i = 0; i < NUM_DIRECTIONS; i++)
14528   {
14529     int test_x, test_y, test_move_dir, test_element;
14530
14531     test_x = good_x + test_xy[i][0];
14532     test_y = good_y + test_xy[i][1];
14533
14534     if (!IN_LEV_FIELD(test_x, test_y))
14535       continue;
14536
14537     test_move_dir =
14538       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14539
14540     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14541
14542     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14543        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14544     */
14545     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14546         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14547     {
14548       kill_x = test_x;
14549       kill_y = test_y;
14550       bad_element = test_element;
14551
14552       break;
14553     }
14554   }
14555
14556   if (kill_x != -1 || kill_y != -1)
14557   {
14558     if (IS_PLAYER(good_x, good_y))
14559     {
14560       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14561
14562       if (player->shield_deadly_time_left > 0 &&
14563           !IS_INDESTRUCTIBLE(bad_element))
14564         Bang(kill_x, kill_y);
14565       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14566         KillPlayer(player);
14567     }
14568     else
14569       Bang(good_x, good_y);
14570   }
14571 }
14572
14573 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14574 {
14575   int i, kill_x = -1, kill_y = -1;
14576   int bad_element = Feld[bad_x][bad_y];
14577   static int test_xy[4][2] =
14578   {
14579     { 0, -1 },
14580     { -1, 0 },
14581     { +1, 0 },
14582     { 0, +1 }
14583   };
14584   static int touch_dir[4] =
14585   {
14586     MV_LEFT | MV_RIGHT,
14587     MV_UP   | MV_DOWN,
14588     MV_UP   | MV_DOWN,
14589     MV_LEFT | MV_RIGHT
14590   };
14591   static int test_dir[4] =
14592   {
14593     MV_UP,
14594     MV_LEFT,
14595     MV_RIGHT,
14596     MV_DOWN
14597   };
14598
14599   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14600     return;
14601
14602   for (i = 0; i < NUM_DIRECTIONS; i++)
14603   {
14604     int test_x, test_y, test_move_dir, test_element;
14605
14606     test_x = bad_x + test_xy[i][0];
14607     test_y = bad_y + test_xy[i][1];
14608
14609     if (!IN_LEV_FIELD(test_x, test_y))
14610       continue;
14611
14612     test_move_dir =
14613       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14614
14615     test_element = Feld[test_x][test_y];
14616
14617     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14618        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14619     */
14620     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14621         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14622     {
14623       /* good thing is player or penguin that does not move away */
14624       if (IS_PLAYER(test_x, test_y))
14625       {
14626         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14627
14628         if (bad_element == EL_ROBOT && player->is_moving)
14629           continue;     /* robot does not kill player if he is moving */
14630
14631         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14632         {
14633           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14634             continue;           /* center and border element do not touch */
14635         }
14636
14637         kill_x = test_x;
14638         kill_y = test_y;
14639
14640         break;
14641       }
14642       else if (test_element == EL_PENGUIN)
14643       {
14644         kill_x = test_x;
14645         kill_y = test_y;
14646
14647         break;
14648       }
14649     }
14650   }
14651
14652   if (kill_x != -1 || kill_y != -1)
14653   {
14654     if (IS_PLAYER(kill_x, kill_y))
14655     {
14656       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14657
14658       if (player->shield_deadly_time_left > 0 &&
14659           !IS_INDESTRUCTIBLE(bad_element))
14660         Bang(bad_x, bad_y);
14661       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14662         KillPlayer(player);
14663     }
14664     else
14665       Bang(kill_x, kill_y);
14666   }
14667 }
14668
14669 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14670 {
14671   int bad_element = Feld[bad_x][bad_y];
14672   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14673   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14674   int test_x = bad_x + dx, test_y = bad_y + dy;
14675   int test_move_dir, test_element;
14676   int kill_x = -1, kill_y = -1;
14677
14678   if (!IN_LEV_FIELD(test_x, test_y))
14679     return;
14680
14681   test_move_dir =
14682     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14683
14684   test_element = Feld[test_x][test_y];
14685
14686   if (test_move_dir != bad_move_dir)
14687   {
14688     /* good thing can be player or penguin that does not move away */
14689     if (IS_PLAYER(test_x, test_y))
14690     {
14691       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14692
14693       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14694          player as being hit when he is moving towards the bad thing, because
14695          the "get hit by" condition would be lost after the player stops) */
14696       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14697         return;         /* player moves away from bad thing */
14698
14699       kill_x = test_x;
14700       kill_y = test_y;
14701     }
14702     else if (test_element == EL_PENGUIN)
14703     {
14704       kill_x = test_x;
14705       kill_y = test_y;
14706     }
14707   }
14708
14709   if (kill_x != -1 || kill_y != -1)
14710   {
14711     if (IS_PLAYER(kill_x, kill_y))
14712     {
14713       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14714
14715       if (player->shield_deadly_time_left > 0 &&
14716           !IS_INDESTRUCTIBLE(bad_element))
14717         Bang(bad_x, bad_y);
14718       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14719         KillPlayer(player);
14720     }
14721     else
14722       Bang(kill_x, kill_y);
14723   }
14724 }
14725
14726 void TestIfPlayerTouchesBadThing(int x, int y)
14727 {
14728   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14729 }
14730
14731 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14732 {
14733   TestIfGoodThingHitsBadThing(x, y, move_dir);
14734 }
14735
14736 void TestIfBadThingTouchesPlayer(int x, int y)
14737 {
14738   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14739 }
14740
14741 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14742 {
14743   TestIfBadThingHitsGoodThing(x, y, move_dir);
14744 }
14745
14746 void TestIfFriendTouchesBadThing(int x, int y)
14747 {
14748   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14749 }
14750
14751 void TestIfBadThingTouchesFriend(int x, int y)
14752 {
14753   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14754 }
14755
14756 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14757 {
14758   int i, kill_x = bad_x, kill_y = bad_y;
14759   static int xy[4][2] =
14760   {
14761     { 0, -1 },
14762     { -1, 0 },
14763     { +1, 0 },
14764     { 0, +1 }
14765   };
14766
14767   for (i = 0; i < NUM_DIRECTIONS; i++)
14768   {
14769     int x, y, element;
14770
14771     x = bad_x + xy[i][0];
14772     y = bad_y + xy[i][1];
14773     if (!IN_LEV_FIELD(x, y))
14774       continue;
14775
14776     element = Feld[x][y];
14777     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14778         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14779     {
14780       kill_x = x;
14781       kill_y = y;
14782       break;
14783     }
14784   }
14785
14786   if (kill_x != bad_x || kill_y != bad_y)
14787     Bang(bad_x, bad_y);
14788 }
14789
14790 void KillPlayer(struct PlayerInfo *player)
14791 {
14792   int jx = player->jx, jy = player->jy;
14793
14794   if (!player->active)
14795     return;
14796
14797 #if 0
14798   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14799          player->killed, player->active, player->reanimated);
14800 #endif
14801
14802   /* the following code was introduced to prevent an infinite loop when calling
14803      -> Bang()
14804      -> CheckTriggeredElementChangeExt()
14805      -> ExecuteCustomElementAction()
14806      -> KillPlayer()
14807      -> (infinitely repeating the above sequence of function calls)
14808      which occurs when killing the player while having a CE with the setting
14809      "kill player X when explosion of <player X>"; the solution using a new
14810      field "player->killed" was chosen for backwards compatibility, although
14811      clever use of the fields "player->active" etc. would probably also work */
14812 #if 1
14813   if (player->killed)
14814     return;
14815 #endif
14816
14817   player->killed = TRUE;
14818
14819   /* remove accessible field at the player's position */
14820   Feld[jx][jy] = EL_EMPTY;
14821
14822   /* deactivate shield (else Bang()/Explode() would not work right) */
14823   player->shield_normal_time_left = 0;
14824   player->shield_deadly_time_left = 0;
14825
14826 #if 0
14827   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14828          player->killed, player->active, player->reanimated);
14829 #endif
14830
14831   Bang(jx, jy);
14832
14833 #if 0
14834   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14835          player->killed, player->active, player->reanimated);
14836 #endif
14837
14838 #if USE_PLAYER_REANIMATION
14839 #if 1
14840   if (player->reanimated)       /* killed player may have been reanimated */
14841     player->killed = player->reanimated = FALSE;
14842   else
14843     BuryPlayer(player);
14844 #else
14845   if (player->killed)           /* player may have been reanimated */
14846     BuryPlayer(player);
14847 #endif
14848 #else
14849   BuryPlayer(player);
14850 #endif
14851 }
14852
14853 static void KillPlayerUnlessEnemyProtected(int x, int y)
14854 {
14855   if (!PLAYER_ENEMY_PROTECTED(x, y))
14856     KillPlayer(PLAYERINFO(x, y));
14857 }
14858
14859 static void KillPlayerUnlessExplosionProtected(int x, int y)
14860 {
14861   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14862     KillPlayer(PLAYERINFO(x, y));
14863 }
14864
14865 void BuryPlayer(struct PlayerInfo *player)
14866 {
14867   int jx = player->jx, jy = player->jy;
14868
14869   if (!player->active)
14870     return;
14871
14872   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14873   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14874
14875   player->GameOver = TRUE;
14876   RemovePlayer(player);
14877 }
14878
14879 void RemovePlayer(struct PlayerInfo *player)
14880 {
14881   int jx = player->jx, jy = player->jy;
14882   int i, found = FALSE;
14883
14884   player->present = FALSE;
14885   player->active = FALSE;
14886
14887   if (!ExplodeField[jx][jy])
14888     StorePlayer[jx][jy] = 0;
14889
14890   if (player->is_moving)
14891     TEST_DrawLevelField(player->last_jx, player->last_jy);
14892
14893   for (i = 0; i < MAX_PLAYERS; i++)
14894     if (stored_player[i].active)
14895       found = TRUE;
14896
14897   if (!found)
14898     AllPlayersGone = TRUE;
14899
14900   ExitX = ZX = jx;
14901   ExitY = ZY = jy;
14902 }
14903
14904 #if USE_NEW_SNAP_DELAY
14905 static void setFieldForSnapping(int x, int y, int element, int direction)
14906 {
14907   struct ElementInfo *ei = &element_info[element];
14908   int direction_bit = MV_DIR_TO_BIT(direction);
14909   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14910   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14911                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14912
14913   Feld[x][y] = EL_ELEMENT_SNAPPING;
14914   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14915
14916   ResetGfxAnimation(x, y);
14917
14918   GfxElement[x][y] = element;
14919   GfxAction[x][y] = action;
14920   GfxDir[x][y] = direction;
14921   GfxFrame[x][y] = -1;
14922 }
14923 #endif
14924
14925 /*
14926   =============================================================================
14927   checkDiagonalPushing()
14928   -----------------------------------------------------------------------------
14929   check if diagonal input device direction results in pushing of object
14930   (by checking if the alternative direction is walkable, diggable, ...)
14931   =============================================================================
14932 */
14933
14934 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14935                                     int x, int y, int real_dx, int real_dy)
14936 {
14937   int jx, jy, dx, dy, xx, yy;
14938
14939   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14940     return TRUE;
14941
14942   /* diagonal direction: check alternative direction */
14943   jx = player->jx;
14944   jy = player->jy;
14945   dx = x - jx;
14946   dy = y - jy;
14947   xx = jx + (dx == 0 ? real_dx : 0);
14948   yy = jy + (dy == 0 ? real_dy : 0);
14949
14950   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14951 }
14952
14953 /*
14954   =============================================================================
14955   DigField()
14956   -----------------------------------------------------------------------------
14957   x, y:                 field next to player (non-diagonal) to try to dig to
14958   real_dx, real_dy:     direction as read from input device (can be diagonal)
14959   =============================================================================
14960 */
14961
14962 static int DigField(struct PlayerInfo *player,
14963                     int oldx, int oldy, int x, int y,
14964                     int real_dx, int real_dy, int mode)
14965 {
14966   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14967   boolean player_was_pushing = player->is_pushing;
14968   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14969   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14970   int jx = oldx, jy = oldy;
14971   int dx = x - jx, dy = y - jy;
14972   int nextx = x + dx, nexty = y + dy;
14973   int move_direction = (dx == -1 ? MV_LEFT  :
14974                         dx == +1 ? MV_RIGHT :
14975                         dy == -1 ? MV_UP    :
14976                         dy == +1 ? MV_DOWN  : MV_NONE);
14977   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14978   int dig_side = MV_DIR_OPPOSITE(move_direction);
14979   int old_element = Feld[jx][jy];
14980 #if USE_FIXED_DONT_RUN_INTO
14981   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14982 #else
14983   int element;
14984 #endif
14985   int collect_count;
14986
14987   if (is_player)                /* function can also be called by EL_PENGUIN */
14988   {
14989     if (player->MovPos == 0)
14990     {
14991       player->is_digging = FALSE;
14992       player->is_collecting = FALSE;
14993     }
14994
14995     if (player->MovPos == 0)    /* last pushing move finished */
14996       player->is_pushing = FALSE;
14997
14998     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14999     {
15000       player->is_switching = FALSE;
15001       player->push_delay = -1;
15002
15003       return MP_NO_ACTION;
15004     }
15005   }
15006
15007 #if !USE_FIXED_DONT_RUN_INTO
15008   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15009     return MP_NO_ACTION;
15010 #endif
15011
15012   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15013     old_element = Back[jx][jy];
15014
15015   /* in case of element dropped at player position, check background */
15016   else if (Back[jx][jy] != EL_EMPTY &&
15017            game.engine_version >= VERSION_IDENT(2,2,0,0))
15018     old_element = Back[jx][jy];
15019
15020   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15021     return MP_NO_ACTION;        /* field has no opening in this direction */
15022
15023   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15024     return MP_NO_ACTION;        /* field has no opening in this direction */
15025
15026 #if USE_FIXED_DONT_RUN_INTO
15027   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15028   {
15029     SplashAcid(x, y);
15030
15031     Feld[jx][jy] = player->artwork_element;
15032     InitMovingField(jx, jy, MV_DOWN);
15033     Store[jx][jy] = EL_ACID;
15034     ContinueMoving(jx, jy);
15035     BuryPlayer(player);
15036
15037     return MP_DONT_RUN_INTO;
15038   }
15039 #endif
15040
15041 #if USE_FIXED_DONT_RUN_INTO
15042   if (player_can_move && DONT_RUN_INTO(element))
15043   {
15044     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15045
15046     return MP_DONT_RUN_INTO;
15047   }
15048 #endif
15049
15050 #if USE_FIXED_DONT_RUN_INTO
15051   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15052     return MP_NO_ACTION;
15053 #endif
15054
15055 #if !USE_FIXED_DONT_RUN_INTO
15056   element = Feld[x][y];
15057 #endif
15058
15059   collect_count = element_info[element].collect_count_initial;
15060
15061   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15062     return MP_NO_ACTION;
15063
15064   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15065     player_can_move = player_can_move_or_snap;
15066
15067   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15068       game.engine_version >= VERSION_IDENT(2,2,0,0))
15069   {
15070     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15071                                player->index_bit, dig_side);
15072     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15073                                         player->index_bit, dig_side);
15074
15075     if (element == EL_DC_LANDMINE)
15076       Bang(x, y);
15077
15078     if (Feld[x][y] != element)          /* field changed by snapping */
15079       return MP_ACTION;
15080
15081     return MP_NO_ACTION;
15082   }
15083
15084 #if USE_PLAYER_GRAVITY
15085   if (player->gravity && is_player && !player->is_auto_moving &&
15086       canFallDown(player) && move_direction != MV_DOWN &&
15087       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15088     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15089 #else
15090   if (game.gravity && is_player && !player->is_auto_moving &&
15091       canFallDown(player) && move_direction != MV_DOWN &&
15092       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15093     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15094 #endif
15095
15096   if (player_can_move &&
15097       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15098   {
15099     int sound_element = SND_ELEMENT(element);
15100     int sound_action = ACTION_WALKING;
15101
15102     if (IS_RND_GATE(element))
15103     {
15104       if (!player->key[RND_GATE_NR(element)])
15105         return MP_NO_ACTION;
15106     }
15107     else if (IS_RND_GATE_GRAY(element))
15108     {
15109       if (!player->key[RND_GATE_GRAY_NR(element)])
15110         return MP_NO_ACTION;
15111     }
15112     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15113     {
15114       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15115         return MP_NO_ACTION;
15116     }
15117     else if (element == EL_EXIT_OPEN ||
15118              element == EL_EM_EXIT_OPEN ||
15119 #if 1
15120              element == EL_EM_EXIT_OPENING ||
15121 #endif
15122              element == EL_STEEL_EXIT_OPEN ||
15123              element == EL_EM_STEEL_EXIT_OPEN ||
15124 #if 1
15125              element == EL_EM_STEEL_EXIT_OPENING ||
15126 #endif
15127              element == EL_SP_EXIT_OPEN ||
15128              element == EL_SP_EXIT_OPENING)
15129     {
15130       sound_action = ACTION_PASSING;    /* player is passing exit */
15131     }
15132     else if (element == EL_EMPTY)
15133     {
15134       sound_action = ACTION_MOVING;             /* nothing to walk on */
15135     }
15136
15137     /* play sound from background or player, whatever is available */
15138     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15139       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15140     else
15141       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15142   }
15143   else if (player_can_move &&
15144            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15145   {
15146     if (!ACCESS_FROM(element, opposite_direction))
15147       return MP_NO_ACTION;      /* field not accessible from this direction */
15148
15149     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15150       return MP_NO_ACTION;
15151
15152     if (IS_EM_GATE(element))
15153     {
15154       if (!player->key[EM_GATE_NR(element)])
15155         return MP_NO_ACTION;
15156     }
15157     else if (IS_EM_GATE_GRAY(element))
15158     {
15159       if (!player->key[EM_GATE_GRAY_NR(element)])
15160         return MP_NO_ACTION;
15161     }
15162     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15163     {
15164       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15165         return MP_NO_ACTION;
15166     }
15167     else if (IS_EMC_GATE(element))
15168     {
15169       if (!player->key[EMC_GATE_NR(element)])
15170         return MP_NO_ACTION;
15171     }
15172     else if (IS_EMC_GATE_GRAY(element))
15173     {
15174       if (!player->key[EMC_GATE_GRAY_NR(element)])
15175         return MP_NO_ACTION;
15176     }
15177     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15178     {
15179       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15180         return MP_NO_ACTION;
15181     }
15182     else if (element == EL_DC_GATE_WHITE ||
15183              element == EL_DC_GATE_WHITE_GRAY ||
15184              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15185     {
15186       if (player->num_white_keys == 0)
15187         return MP_NO_ACTION;
15188
15189       player->num_white_keys--;
15190     }
15191     else if (IS_SP_PORT(element))
15192     {
15193       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15194           element == EL_SP_GRAVITY_PORT_RIGHT ||
15195           element == EL_SP_GRAVITY_PORT_UP ||
15196           element == EL_SP_GRAVITY_PORT_DOWN)
15197 #if USE_PLAYER_GRAVITY
15198         player->gravity = !player->gravity;
15199 #else
15200         game.gravity = !game.gravity;
15201 #endif
15202       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15203                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15204                element == EL_SP_GRAVITY_ON_PORT_UP ||
15205                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15206 #if USE_PLAYER_GRAVITY
15207         player->gravity = TRUE;
15208 #else
15209         game.gravity = TRUE;
15210 #endif
15211       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15212                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15213                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15214                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15215 #if USE_PLAYER_GRAVITY
15216         player->gravity = FALSE;
15217 #else
15218         game.gravity = FALSE;
15219 #endif
15220     }
15221
15222     /* automatically move to the next field with double speed */
15223     player->programmed_action = move_direction;
15224
15225     if (player->move_delay_reset_counter == 0)
15226     {
15227       player->move_delay_reset_counter = 2;     /* two double speed steps */
15228
15229       DOUBLE_PLAYER_SPEED(player);
15230     }
15231
15232     PlayLevelSoundAction(x, y, ACTION_PASSING);
15233   }
15234   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15235   {
15236     RemoveField(x, y);
15237
15238     if (mode != DF_SNAP)
15239     {
15240       GfxElement[x][y] = GFX_ELEMENT(element);
15241       player->is_digging = TRUE;
15242     }
15243
15244     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15245
15246     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15247                                         player->index_bit, dig_side);
15248
15249     if (mode == DF_SNAP)
15250     {
15251 #if USE_NEW_SNAP_DELAY
15252       if (level.block_snap_field)
15253         setFieldForSnapping(x, y, element, move_direction);
15254       else
15255         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15256 #else
15257       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15258 #endif
15259
15260       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15261                                           player->index_bit, dig_side);
15262     }
15263   }
15264   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15265   {
15266     RemoveField(x, y);
15267
15268     if (is_player && mode != DF_SNAP)
15269     {
15270       GfxElement[x][y] = element;
15271       player->is_collecting = TRUE;
15272     }
15273
15274     if (element == EL_SPEED_PILL)
15275     {
15276       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15277     }
15278     else if (element == EL_EXTRA_TIME && level.time > 0)
15279     {
15280       TimeLeft += level.extra_time;
15281
15282 #if 1
15283       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15284
15285       DisplayGameControlValues();
15286 #else
15287       DrawGameValue_Time(TimeLeft);
15288 #endif
15289     }
15290     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15291     {
15292       player->shield_normal_time_left += level.shield_normal_time;
15293       if (element == EL_SHIELD_DEADLY)
15294         player->shield_deadly_time_left += level.shield_deadly_time;
15295     }
15296     else if (element == EL_DYNAMITE ||
15297              element == EL_EM_DYNAMITE ||
15298              element == EL_SP_DISK_RED)
15299     {
15300       if (player->inventory_size < MAX_INVENTORY_SIZE)
15301         player->inventory_element[player->inventory_size++] = element;
15302
15303       DrawGameDoorValues();
15304     }
15305     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15306     {
15307       player->dynabomb_count++;
15308       player->dynabombs_left++;
15309     }
15310     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15311     {
15312       player->dynabomb_size++;
15313     }
15314     else if (element == EL_DYNABOMB_INCREASE_POWER)
15315     {
15316       player->dynabomb_xl = TRUE;
15317     }
15318     else if (IS_KEY(element))
15319     {
15320       player->key[KEY_NR(element)] = TRUE;
15321
15322       DrawGameDoorValues();
15323     }
15324     else if (element == EL_DC_KEY_WHITE)
15325     {
15326       player->num_white_keys++;
15327
15328       /* display white keys? */
15329       /* DrawGameDoorValues(); */
15330     }
15331     else if (IS_ENVELOPE(element))
15332     {
15333       player->show_envelope = element;
15334     }
15335     else if (element == EL_EMC_LENSES)
15336     {
15337       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15338
15339       RedrawAllInvisibleElementsForLenses();
15340     }
15341     else if (element == EL_EMC_MAGNIFIER)
15342     {
15343       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15344
15345       RedrawAllInvisibleElementsForMagnifier();
15346     }
15347     else if (IS_DROPPABLE(element) ||
15348              IS_THROWABLE(element))     /* can be collected and dropped */
15349     {
15350       int i;
15351
15352       if (collect_count == 0)
15353         player->inventory_infinite_element = element;
15354       else
15355         for (i = 0; i < collect_count; i++)
15356           if (player->inventory_size < MAX_INVENTORY_SIZE)
15357             player->inventory_element[player->inventory_size++] = element;
15358
15359       DrawGameDoorValues();
15360     }
15361     else if (collect_count > 0)
15362     {
15363       local_player->gems_still_needed -= collect_count;
15364       if (local_player->gems_still_needed < 0)
15365         local_player->gems_still_needed = 0;
15366
15367 #if 1
15368       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15369
15370       DisplayGameControlValues();
15371 #else
15372       DrawGameValue_Emeralds(local_player->gems_still_needed);
15373 #endif
15374     }
15375
15376     RaiseScoreElement(element);
15377     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15378
15379     if (is_player)
15380       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15381                                           player->index_bit, dig_side);
15382
15383     if (mode == DF_SNAP)
15384     {
15385 #if USE_NEW_SNAP_DELAY
15386       if (level.block_snap_field)
15387         setFieldForSnapping(x, y, element, move_direction);
15388       else
15389         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15390 #else
15391       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15392 #endif
15393
15394       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15395                                           player->index_bit, dig_side);
15396     }
15397   }
15398   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15399   {
15400     if (mode == DF_SNAP && element != EL_BD_ROCK)
15401       return MP_NO_ACTION;
15402
15403     if (CAN_FALL(element) && dy)
15404       return MP_NO_ACTION;
15405
15406     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15407         !(element == EL_SPRING && level.use_spring_bug))
15408       return MP_NO_ACTION;
15409
15410     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15411         ((move_direction & MV_VERTICAL &&
15412           ((element_info[element].move_pattern & MV_LEFT &&
15413             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15414            (element_info[element].move_pattern & MV_RIGHT &&
15415             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15416          (move_direction & MV_HORIZONTAL &&
15417           ((element_info[element].move_pattern & MV_UP &&
15418             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15419            (element_info[element].move_pattern & MV_DOWN &&
15420             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15421       return MP_NO_ACTION;
15422
15423     /* do not push elements already moving away faster than player */
15424     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15425         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15426       return MP_NO_ACTION;
15427
15428     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15429     {
15430       if (player->push_delay_value == -1 || !player_was_pushing)
15431         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15432     }
15433     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15434     {
15435       if (player->push_delay_value == -1)
15436         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15437     }
15438     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15439     {
15440       if (!player->is_pushing)
15441         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15442     }
15443
15444     player->is_pushing = TRUE;
15445     player->is_active = TRUE;
15446
15447     if (!(IN_LEV_FIELD(nextx, nexty) &&
15448           (IS_FREE(nextx, nexty) ||
15449            (IS_SB_ELEMENT(element) &&
15450             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15451            (IS_CUSTOM_ELEMENT(element) &&
15452             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15453       return MP_NO_ACTION;
15454
15455     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15456       return MP_NO_ACTION;
15457
15458     if (player->push_delay == -1)       /* new pushing; restart delay */
15459       player->push_delay = 0;
15460
15461     if (player->push_delay < player->push_delay_value &&
15462         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15463         element != EL_SPRING && element != EL_BALLOON)
15464     {
15465       /* make sure that there is no move delay before next try to push */
15466       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15467         player->move_delay = 0;
15468
15469       return MP_NO_ACTION;
15470     }
15471
15472     if (IS_CUSTOM_ELEMENT(element) &&
15473         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15474     {
15475       if (!DigFieldByCE(nextx, nexty, element))
15476         return MP_NO_ACTION;
15477     }
15478
15479     if (IS_SB_ELEMENT(element))
15480     {
15481       if (element == EL_SOKOBAN_FIELD_FULL)
15482       {
15483         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15484         local_player->sokobanfields_still_needed++;
15485       }
15486
15487       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15488       {
15489         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15490         local_player->sokobanfields_still_needed--;
15491       }
15492
15493       Feld[x][y] = EL_SOKOBAN_OBJECT;
15494
15495       if (Back[x][y] == Back[nextx][nexty])
15496         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15497       else if (Back[x][y] != 0)
15498         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15499                                     ACTION_EMPTYING);
15500       else
15501         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15502                                     ACTION_FILLING);
15503
15504 #if 1
15505       if (local_player->sokobanfields_still_needed == 0 &&
15506           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15507 #else
15508       if (local_player->sokobanfields_still_needed == 0 &&
15509           game.emulation == EMU_SOKOBAN)
15510 #endif
15511       {
15512         PlayerWins(player);
15513
15514         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15515       }
15516     }
15517     else
15518       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15519
15520     InitMovingField(x, y, move_direction);
15521     GfxAction[x][y] = ACTION_PUSHING;
15522
15523     if (mode == DF_SNAP)
15524       ContinueMoving(x, y);
15525     else
15526       MovPos[x][y] = (dx != 0 ? dx : dy);
15527
15528     Pushed[x][y] = TRUE;
15529     Pushed[nextx][nexty] = TRUE;
15530
15531     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15532       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15533     else
15534       player->push_delay_value = -1;    /* get new value later */
15535
15536     /* check for element change _after_ element has been pushed */
15537     if (game.use_change_when_pushing_bug)
15538     {
15539       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15540                                  player->index_bit, dig_side);
15541       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15542                                           player->index_bit, dig_side);
15543     }
15544   }
15545   else if (IS_SWITCHABLE(element))
15546   {
15547     if (PLAYER_SWITCHING(player, x, y))
15548     {
15549       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15550                                           player->index_bit, dig_side);
15551
15552       return MP_ACTION;
15553     }
15554
15555     player->is_switching = TRUE;
15556     player->switch_x = x;
15557     player->switch_y = y;
15558
15559     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15560
15561     if (element == EL_ROBOT_WHEEL)
15562     {
15563       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15564       ZX = x;
15565       ZY = y;
15566
15567       game.robot_wheel_active = TRUE;
15568
15569       TEST_DrawLevelField(x, y);
15570     }
15571     else if (element == EL_SP_TERMINAL)
15572     {
15573       int xx, yy;
15574
15575       SCAN_PLAYFIELD(xx, yy)
15576       {
15577         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15578           Bang(xx, yy);
15579         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15580           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15581       }
15582     }
15583     else if (IS_BELT_SWITCH(element))
15584     {
15585       ToggleBeltSwitch(x, y);
15586     }
15587     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15588              element == EL_SWITCHGATE_SWITCH_DOWN ||
15589              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15590              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15591     {
15592       ToggleSwitchgateSwitch(x, y);
15593     }
15594     else if (element == EL_LIGHT_SWITCH ||
15595              element == EL_LIGHT_SWITCH_ACTIVE)
15596     {
15597       ToggleLightSwitch(x, y);
15598     }
15599     else if (element == EL_TIMEGATE_SWITCH ||
15600              element == EL_DC_TIMEGATE_SWITCH)
15601     {
15602       ActivateTimegateSwitch(x, y);
15603     }
15604     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15605              element == EL_BALLOON_SWITCH_RIGHT ||
15606              element == EL_BALLOON_SWITCH_UP    ||
15607              element == EL_BALLOON_SWITCH_DOWN  ||
15608              element == EL_BALLOON_SWITCH_NONE  ||
15609              element == EL_BALLOON_SWITCH_ANY)
15610     {
15611       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15612                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15613                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15614                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15615                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15616                              move_direction);
15617     }
15618     else if (element == EL_LAMP)
15619     {
15620       Feld[x][y] = EL_LAMP_ACTIVE;
15621       local_player->lights_still_needed--;
15622
15623       ResetGfxAnimation(x, y);
15624       TEST_DrawLevelField(x, y);
15625     }
15626     else if (element == EL_TIME_ORB_FULL)
15627     {
15628       Feld[x][y] = EL_TIME_ORB_EMPTY;
15629
15630       if (level.time > 0 || level.use_time_orb_bug)
15631       {
15632         TimeLeft += level.time_orb_time;
15633
15634 #if 1
15635         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15636
15637         DisplayGameControlValues();
15638 #else
15639         DrawGameValue_Time(TimeLeft);
15640 #endif
15641       }
15642
15643       ResetGfxAnimation(x, y);
15644       TEST_DrawLevelField(x, y);
15645     }
15646     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15647              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15648     {
15649       int xx, yy;
15650
15651       game.ball_state = !game.ball_state;
15652
15653       SCAN_PLAYFIELD(xx, yy)
15654       {
15655         int e = Feld[xx][yy];
15656
15657         if (game.ball_state)
15658         {
15659           if (e == EL_EMC_MAGIC_BALL)
15660             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15661           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15662             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15663         }
15664         else
15665         {
15666           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15667             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15668           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15669             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15670         }
15671       }
15672     }
15673
15674     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15675                                         player->index_bit, dig_side);
15676
15677     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15678                                         player->index_bit, dig_side);
15679
15680     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15681                                         player->index_bit, dig_side);
15682
15683     return MP_ACTION;
15684   }
15685   else
15686   {
15687     if (!PLAYER_SWITCHING(player, x, y))
15688     {
15689       player->is_switching = TRUE;
15690       player->switch_x = x;
15691       player->switch_y = y;
15692
15693       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15694                                  player->index_bit, dig_side);
15695       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15696                                           player->index_bit, dig_side);
15697
15698       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15699                                  player->index_bit, dig_side);
15700       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15701                                           player->index_bit, dig_side);
15702     }
15703
15704     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15705                                player->index_bit, dig_side);
15706     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15707                                         player->index_bit, dig_side);
15708
15709     return MP_NO_ACTION;
15710   }
15711
15712   player->push_delay = -1;
15713
15714   if (is_player)                /* function can also be called by EL_PENGUIN */
15715   {
15716     if (Feld[x][y] != element)          /* really digged/collected something */
15717     {
15718       player->is_collecting = !player->is_digging;
15719       player->is_active = TRUE;
15720     }
15721   }
15722
15723   return MP_MOVING;
15724 }
15725
15726 static boolean DigFieldByCE(int x, int y, int digging_element)
15727 {
15728   int element = Feld[x][y];
15729
15730   if (!IS_FREE(x, y))
15731   {
15732     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15733                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15734                   ACTION_BREAKING);
15735
15736     /* no element can dig solid indestructible elements */
15737     if (IS_INDESTRUCTIBLE(element) &&
15738         !IS_DIGGABLE(element) &&
15739         !IS_COLLECTIBLE(element))
15740       return FALSE;
15741
15742     if (AmoebaNr[x][y] &&
15743         (element == EL_AMOEBA_FULL ||
15744          element == EL_BD_AMOEBA ||
15745          element == EL_AMOEBA_GROWING))
15746     {
15747       AmoebaCnt[AmoebaNr[x][y]]--;
15748       AmoebaCnt2[AmoebaNr[x][y]]--;
15749     }
15750
15751     if (IS_MOVING(x, y))
15752       RemoveMovingField(x, y);
15753     else
15754     {
15755       RemoveField(x, y);
15756       TEST_DrawLevelField(x, y);
15757     }
15758
15759     /* if digged element was about to explode, prevent the explosion */
15760     ExplodeField[x][y] = EX_TYPE_NONE;
15761
15762     PlayLevelSoundAction(x, y, action);
15763   }
15764
15765   Store[x][y] = EL_EMPTY;
15766
15767 #if 1
15768   /* this makes it possible to leave the removed element again */
15769   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15770     Store[x][y] = element;
15771 #else
15772   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15773   {
15774     int move_leave_element = element_info[digging_element].move_leave_element;
15775
15776     /* this makes it possible to leave the removed element again */
15777     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15778                    element : move_leave_element);
15779   }
15780 #endif
15781
15782   return TRUE;
15783 }
15784
15785 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15786 {
15787   int jx = player->jx, jy = player->jy;
15788   int x = jx + dx, y = jy + dy;
15789   int snap_direction = (dx == -1 ? MV_LEFT  :
15790                         dx == +1 ? MV_RIGHT :
15791                         dy == -1 ? MV_UP    :
15792                         dy == +1 ? MV_DOWN  : MV_NONE);
15793   boolean can_continue_snapping = (level.continuous_snapping &&
15794                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15795
15796   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15797     return FALSE;
15798
15799   if (!player->active || !IN_LEV_FIELD(x, y))
15800     return FALSE;
15801
15802   if (dx && dy)
15803     return FALSE;
15804
15805   if (!dx && !dy)
15806   {
15807     if (player->MovPos == 0)
15808       player->is_pushing = FALSE;
15809
15810     player->is_snapping = FALSE;
15811
15812     if (player->MovPos == 0)
15813     {
15814       player->is_moving = FALSE;
15815       player->is_digging = FALSE;
15816       player->is_collecting = FALSE;
15817     }
15818
15819     return FALSE;
15820   }
15821
15822 #if USE_NEW_CONTINUOUS_SNAPPING
15823   /* prevent snapping with already pressed snap key when not allowed */
15824   if (player->is_snapping && !can_continue_snapping)
15825     return FALSE;
15826 #else
15827   if (player->is_snapping)
15828     return FALSE;
15829 #endif
15830
15831   player->MovDir = snap_direction;
15832
15833   if (player->MovPos == 0)
15834   {
15835     player->is_moving = FALSE;
15836     player->is_digging = FALSE;
15837     player->is_collecting = FALSE;
15838   }
15839
15840   player->is_dropping = FALSE;
15841   player->is_dropping_pressed = FALSE;
15842   player->drop_pressed_delay = 0;
15843
15844   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15845     return FALSE;
15846
15847   player->is_snapping = TRUE;
15848   player->is_active = TRUE;
15849
15850   if (player->MovPos == 0)
15851   {
15852     player->is_moving = FALSE;
15853     player->is_digging = FALSE;
15854     player->is_collecting = FALSE;
15855   }
15856
15857   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15858     TEST_DrawLevelField(player->last_jx, player->last_jy);
15859
15860   TEST_DrawLevelField(x, y);
15861
15862   return TRUE;
15863 }
15864
15865 static boolean DropElement(struct PlayerInfo *player)
15866 {
15867   int old_element, new_element;
15868   int dropx = player->jx, dropy = player->jy;
15869   int drop_direction = player->MovDir;
15870   int drop_side = drop_direction;
15871 #if 1
15872   int drop_element = get_next_dropped_element(player);
15873 #else
15874   int drop_element = (player->inventory_size > 0 ?
15875                       player->inventory_element[player->inventory_size - 1] :
15876                       player->inventory_infinite_element != EL_UNDEFINED ?
15877                       player->inventory_infinite_element :
15878                       player->dynabombs_left > 0 ?
15879                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15880                       EL_UNDEFINED);
15881 #endif
15882
15883   player->is_dropping_pressed = TRUE;
15884
15885   /* do not drop an element on top of another element; when holding drop key
15886      pressed without moving, dropped element must move away before the next
15887      element can be dropped (this is especially important if the next element
15888      is dynamite, which can be placed on background for historical reasons) */
15889   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15890     return MP_ACTION;
15891
15892   if (IS_THROWABLE(drop_element))
15893   {
15894     dropx += GET_DX_FROM_DIR(drop_direction);
15895     dropy += GET_DY_FROM_DIR(drop_direction);
15896
15897     if (!IN_LEV_FIELD(dropx, dropy))
15898       return FALSE;
15899   }
15900
15901   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15902   new_element = drop_element;           /* default: no change when dropping */
15903
15904   /* check if player is active, not moving and ready to drop */
15905   if (!player->active || player->MovPos || player->drop_delay > 0)
15906     return FALSE;
15907
15908   /* check if player has anything that can be dropped */
15909   if (new_element == EL_UNDEFINED)
15910     return FALSE;
15911
15912   /* check if drop key was pressed long enough for EM style dynamite */
15913   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15914     return FALSE;
15915
15916   /* check if anything can be dropped at the current position */
15917   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15918     return FALSE;
15919
15920   /* collected custom elements can only be dropped on empty fields */
15921   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15922     return FALSE;
15923
15924   if (old_element != EL_EMPTY)
15925     Back[dropx][dropy] = old_element;   /* store old element on this field */
15926
15927   ResetGfxAnimation(dropx, dropy);
15928   ResetRandomAnimationValue(dropx, dropy);
15929
15930   if (player->inventory_size > 0 ||
15931       player->inventory_infinite_element != EL_UNDEFINED)
15932   {
15933     if (player->inventory_size > 0)
15934     {
15935       player->inventory_size--;
15936
15937       DrawGameDoorValues();
15938
15939       if (new_element == EL_DYNAMITE)
15940         new_element = EL_DYNAMITE_ACTIVE;
15941       else if (new_element == EL_EM_DYNAMITE)
15942         new_element = EL_EM_DYNAMITE_ACTIVE;
15943       else if (new_element == EL_SP_DISK_RED)
15944         new_element = EL_SP_DISK_RED_ACTIVE;
15945     }
15946
15947     Feld[dropx][dropy] = new_element;
15948
15949     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15950       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15951                           el2img(Feld[dropx][dropy]), 0);
15952
15953     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15954
15955     /* needed if previous element just changed to "empty" in the last frame */
15956     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15957
15958     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15959                                player->index_bit, drop_side);
15960     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15961                                         CE_PLAYER_DROPS_X,
15962                                         player->index_bit, drop_side);
15963
15964     TestIfElementTouchesCustomElement(dropx, dropy);
15965   }
15966   else          /* player is dropping a dyna bomb */
15967   {
15968     player->dynabombs_left--;
15969
15970     Feld[dropx][dropy] = new_element;
15971
15972     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15973       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15974                           el2img(Feld[dropx][dropy]), 0);
15975
15976     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15977   }
15978
15979   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15980     InitField_WithBug1(dropx, dropy, FALSE);
15981
15982   new_element = Feld[dropx][dropy];     /* element might have changed */
15983
15984   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15985       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15986   {
15987     int move_direction, nextx, nexty;
15988
15989     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15990       MovDir[dropx][dropy] = drop_direction;
15991
15992     move_direction = MovDir[dropx][dropy];
15993     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15994     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15995
15996     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15997
15998 #if USE_FIX_IMPACT_COLLISION
15999     /* do not cause impact style collision by dropping elements that can fall */
16000     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16001 #else
16002     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16003 #endif
16004   }
16005
16006   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16007   player->is_dropping = TRUE;
16008
16009   player->drop_pressed_delay = 0;
16010   player->is_dropping_pressed = FALSE;
16011
16012   player->drop_x = dropx;
16013   player->drop_y = dropy;
16014
16015   return TRUE;
16016 }
16017
16018 /* ------------------------------------------------------------------------- */
16019 /* game sound playing functions                                              */
16020 /* ------------------------------------------------------------------------- */
16021
16022 static int *loop_sound_frame = NULL;
16023 static int *loop_sound_volume = NULL;
16024
16025 void InitPlayLevelSound()
16026 {
16027   int num_sounds = getSoundListSize();
16028
16029   checked_free(loop_sound_frame);
16030   checked_free(loop_sound_volume);
16031
16032   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16033   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16034 }
16035
16036 static void PlayLevelSound(int x, int y, int nr)
16037 {
16038   int sx = SCREENX(x), sy = SCREENY(y);
16039   int volume, stereo_position;
16040   int max_distance = 8;
16041   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16042
16043   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16044       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16045     return;
16046
16047   if (!IN_LEV_FIELD(x, y) ||
16048       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16049       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16050     return;
16051
16052   volume = SOUND_MAX_VOLUME;
16053
16054   if (!IN_SCR_FIELD(sx, sy))
16055   {
16056     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16057     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16058
16059     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16060   }
16061
16062   stereo_position = (SOUND_MAX_LEFT +
16063                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16064                      (SCR_FIELDX + 2 * max_distance));
16065
16066   if (IS_LOOP_SOUND(nr))
16067   {
16068     /* This assures that quieter loop sounds do not overwrite louder ones,
16069        while restarting sound volume comparison with each new game frame. */
16070
16071     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16072       return;
16073
16074     loop_sound_volume[nr] = volume;
16075     loop_sound_frame[nr] = FrameCounter;
16076   }
16077
16078   PlaySoundExt(nr, volume, stereo_position, type);
16079 }
16080
16081 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16082 {
16083   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16084                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16085                  y < LEVELY(BY1) ? LEVELY(BY1) :
16086                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16087                  sound_action);
16088 }
16089
16090 static void PlayLevelSoundAction(int x, int y, int action)
16091 {
16092   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16093 }
16094
16095 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16096 {
16097   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16098
16099   if (sound_effect != SND_UNDEFINED)
16100     PlayLevelSound(x, y, sound_effect);
16101 }
16102
16103 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16104                                               int action)
16105 {
16106   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16107
16108   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16109     PlayLevelSound(x, y, sound_effect);
16110 }
16111
16112 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16113 {
16114   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16115
16116   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16117     PlayLevelSound(x, y, sound_effect);
16118 }
16119
16120 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16121 {
16122   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16123
16124   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16125     StopSound(sound_effect);
16126 }
16127
16128 static void PlayLevelMusic()
16129 {
16130   if (levelset.music[level_nr] != MUS_UNDEFINED)
16131     PlayMusic(levelset.music[level_nr]);        /* from config file */
16132   else
16133     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16134 }
16135
16136 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16137 {
16138   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16139   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16140   int x = xx - 1 - offset;
16141   int y = yy - 1 - offset;
16142
16143   switch (sample)
16144   {
16145     case SAMPLE_blank:
16146       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16147       break;
16148
16149     case SAMPLE_roll:
16150       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16151       break;
16152
16153     case SAMPLE_stone:
16154       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16155       break;
16156
16157     case SAMPLE_nut:
16158       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16159       break;
16160
16161     case SAMPLE_crack:
16162       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16163       break;
16164
16165     case SAMPLE_bug:
16166       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16167       break;
16168
16169     case SAMPLE_tank:
16170       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16171       break;
16172
16173     case SAMPLE_android_clone:
16174       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16175       break;
16176
16177     case SAMPLE_android_move:
16178       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16179       break;
16180
16181     case SAMPLE_spring:
16182       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16183       break;
16184
16185     case SAMPLE_slurp:
16186       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16187       break;
16188
16189     case SAMPLE_eater:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16191       break;
16192
16193     case SAMPLE_eater_eat:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16195       break;
16196
16197     case SAMPLE_alien:
16198       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16199       break;
16200
16201     case SAMPLE_collect:
16202       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16203       break;
16204
16205     case SAMPLE_diamond:
16206       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16207       break;
16208
16209     case SAMPLE_squash:
16210       /* !!! CHECK THIS !!! */
16211 #if 1
16212       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16213 #else
16214       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16215 #endif
16216       break;
16217
16218     case SAMPLE_wonderfall:
16219       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16220       break;
16221
16222     case SAMPLE_drip:
16223       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16224       break;
16225
16226     case SAMPLE_push:
16227       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16228       break;
16229
16230     case SAMPLE_dirt:
16231       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16232       break;
16233
16234     case SAMPLE_acid:
16235       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16236       break;
16237
16238     case SAMPLE_ball:
16239       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16240       break;
16241
16242     case SAMPLE_grow:
16243       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16244       break;
16245
16246     case SAMPLE_wonder:
16247       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16248       break;
16249
16250     case SAMPLE_door:
16251       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16252       break;
16253
16254     case SAMPLE_exit_open:
16255       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16256       break;
16257
16258     case SAMPLE_exit_leave:
16259       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16260       break;
16261
16262     case SAMPLE_dynamite:
16263       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16264       break;
16265
16266     case SAMPLE_tick:
16267       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16268       break;
16269
16270     case SAMPLE_press:
16271       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16272       break;
16273
16274     case SAMPLE_wheel:
16275       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16276       break;
16277
16278     case SAMPLE_boom:
16279       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16280       break;
16281
16282     case SAMPLE_die:
16283       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16284       break;
16285
16286     case SAMPLE_time:
16287       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16288       break;
16289
16290     default:
16291       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16292       break;
16293   }
16294 }
16295
16296 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16297 {
16298   int element = map_element_SP_to_RND(element_sp);
16299   int action = map_action_SP_to_RND(action_sp);
16300   int offset = (setup.sp_show_border_elements ? 0 : 1);
16301   int x = xx - offset;
16302   int y = yy - offset;
16303
16304 #if 0
16305   printf("::: %d -> %d\n", element_sp, action_sp);
16306 #endif
16307
16308   PlayLevelSoundElementAction(x, y, element, action);
16309 }
16310
16311 #if 0
16312 void ChangeTime(int value)
16313 {
16314   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16315
16316   *time += value;
16317
16318   /* EMC game engine uses value from time counter of RND game engine */
16319   level.native_em_level->lev->time = *time;
16320
16321   DrawGameValue_Time(*time);
16322 }
16323
16324 void RaiseScore(int value)
16325 {
16326   /* EMC game engine and RND game engine have separate score counters */
16327   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16328                 &level.native_em_level->lev->score : &local_player->score);
16329
16330   *score += value;
16331
16332   DrawGameValue_Score(*score);
16333 }
16334 #endif
16335
16336 void RaiseScore(int value)
16337 {
16338   local_player->score += value;
16339
16340 #if 1
16341   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16342
16343   DisplayGameControlValues();
16344 #else
16345   DrawGameValue_Score(local_player->score);
16346 #endif
16347 }
16348
16349 void RaiseScoreElement(int element)
16350 {
16351   switch (element)
16352   {
16353     case EL_EMERALD:
16354     case EL_BD_DIAMOND:
16355     case EL_EMERALD_YELLOW:
16356     case EL_EMERALD_RED:
16357     case EL_EMERALD_PURPLE:
16358     case EL_SP_INFOTRON:
16359       RaiseScore(level.score[SC_EMERALD]);
16360       break;
16361     case EL_DIAMOND:
16362       RaiseScore(level.score[SC_DIAMOND]);
16363       break;
16364     case EL_CRYSTAL:
16365       RaiseScore(level.score[SC_CRYSTAL]);
16366       break;
16367     case EL_PEARL:
16368       RaiseScore(level.score[SC_PEARL]);
16369       break;
16370     case EL_BUG:
16371     case EL_BD_BUTTERFLY:
16372     case EL_SP_ELECTRON:
16373       RaiseScore(level.score[SC_BUG]);
16374       break;
16375     case EL_SPACESHIP:
16376     case EL_BD_FIREFLY:
16377     case EL_SP_SNIKSNAK:
16378       RaiseScore(level.score[SC_SPACESHIP]);
16379       break;
16380     case EL_YAMYAM:
16381     case EL_DARK_YAMYAM:
16382       RaiseScore(level.score[SC_YAMYAM]);
16383       break;
16384     case EL_ROBOT:
16385       RaiseScore(level.score[SC_ROBOT]);
16386       break;
16387     case EL_PACMAN:
16388       RaiseScore(level.score[SC_PACMAN]);
16389       break;
16390     case EL_NUT:
16391       RaiseScore(level.score[SC_NUT]);
16392       break;
16393     case EL_DYNAMITE:
16394     case EL_EM_DYNAMITE:
16395     case EL_SP_DISK_RED:
16396     case EL_DYNABOMB_INCREASE_NUMBER:
16397     case EL_DYNABOMB_INCREASE_SIZE:
16398     case EL_DYNABOMB_INCREASE_POWER:
16399       RaiseScore(level.score[SC_DYNAMITE]);
16400       break;
16401     case EL_SHIELD_NORMAL:
16402     case EL_SHIELD_DEADLY:
16403       RaiseScore(level.score[SC_SHIELD]);
16404       break;
16405     case EL_EXTRA_TIME:
16406       RaiseScore(level.extra_time_score);
16407       break;
16408     case EL_KEY_1:
16409     case EL_KEY_2:
16410     case EL_KEY_3:
16411     case EL_KEY_4:
16412     case EL_EM_KEY_1:
16413     case EL_EM_KEY_2:
16414     case EL_EM_KEY_3:
16415     case EL_EM_KEY_4:
16416     case EL_EMC_KEY_5:
16417     case EL_EMC_KEY_6:
16418     case EL_EMC_KEY_7:
16419     case EL_EMC_KEY_8:
16420     case EL_DC_KEY_WHITE:
16421       RaiseScore(level.score[SC_KEY]);
16422       break;
16423     default:
16424       RaiseScore(element_info[element].collect_score);
16425       break;
16426   }
16427 }
16428
16429 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16430 {
16431   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16432   {
16433 #if defined(NETWORK_AVALIABLE)
16434     if (options.network)
16435       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16436     else
16437 #endif
16438     {
16439       if (quick_quit)
16440       {
16441 #if 1
16442
16443 #if 1
16444         FadeSkipNextFadeIn();
16445 #else
16446         fading = fading_none;
16447 #endif
16448
16449 #else
16450         OpenDoor(DOOR_CLOSE_1);
16451 #endif
16452
16453         game_status = GAME_MODE_MAIN;
16454
16455 #if 1
16456         DrawAndFadeInMainMenu(REDRAW_FIELD);
16457 #else
16458         DrawMainMenu();
16459 #endif
16460       }
16461       else
16462       {
16463 #if 0
16464         FadeOut(REDRAW_FIELD);
16465 #endif
16466
16467         game_status = GAME_MODE_MAIN;
16468
16469         DrawAndFadeInMainMenu(REDRAW_FIELD);
16470       }
16471     }
16472   }
16473   else          /* continue playing the game */
16474   {
16475     if (tape.playing && tape.deactivate_display)
16476       TapeDeactivateDisplayOff(TRUE);
16477
16478     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16479
16480     if (tape.playing && tape.deactivate_display)
16481       TapeDeactivateDisplayOn();
16482   }
16483 }
16484
16485 void RequestQuitGame(boolean ask_if_really_quit)
16486 {
16487   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16488   boolean skip_request = AllPlayersGone || quick_quit;
16489
16490   RequestQuitGameExt(skip_request, quick_quit,
16491                      "Do you really want to quit the game ?");
16492 }
16493
16494
16495 /* ------------------------------------------------------------------------- */
16496 /* random generator functions                                                */
16497 /* ------------------------------------------------------------------------- */
16498
16499 unsigned int InitEngineRandom_RND(long seed)
16500 {
16501   game.num_random_calls = 0;
16502
16503 #if 0
16504   unsigned int rnd_seed = InitEngineRandom(seed);
16505
16506   printf("::: START RND: %d\n", rnd_seed);
16507
16508   return rnd_seed;
16509 #else
16510
16511   return InitEngineRandom(seed);
16512
16513 #endif
16514
16515 }
16516
16517 unsigned int RND(int max)
16518 {
16519   if (max > 0)
16520   {
16521     game.num_random_calls++;
16522
16523     return GetEngineRandom(max);
16524   }
16525
16526   return 0;
16527 }
16528
16529
16530 /* ------------------------------------------------------------------------- */
16531 /* game engine snapshot handling functions                                   */
16532 /* ------------------------------------------------------------------------- */
16533
16534 struct EngineSnapshotInfo
16535 {
16536   /* runtime values for custom element collect score */
16537   int collect_score[NUM_CUSTOM_ELEMENTS];
16538
16539   /* runtime values for group element choice position */
16540   int choice_pos[NUM_GROUP_ELEMENTS];
16541
16542   /* runtime values for belt position animations */
16543   int belt_graphic[4][NUM_BELT_PARTS];
16544   int belt_anim_mode[4][NUM_BELT_PARTS];
16545 };
16546
16547 static struct EngineSnapshotInfo engine_snapshot_rnd;
16548 static char *snapshot_level_identifier = NULL;
16549 static int snapshot_level_nr = -1;
16550
16551 static void SaveEngineSnapshotValues_RND()
16552 {
16553   static int belt_base_active_element[4] =
16554   {
16555     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16556     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16557     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16558     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16559   };
16560   int i, j;
16561
16562   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16563   {
16564     int element = EL_CUSTOM_START + i;
16565
16566     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16567   }
16568
16569   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16570   {
16571     int element = EL_GROUP_START + i;
16572
16573     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16574   }
16575
16576   for (i = 0; i < 4; i++)
16577   {
16578     for (j = 0; j < NUM_BELT_PARTS; j++)
16579     {
16580       int element = belt_base_active_element[i] + j;
16581       int graphic = el2img(element);
16582       int anim_mode = graphic_info[graphic].anim_mode;
16583
16584       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16585       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16586     }
16587   }
16588 }
16589
16590 static void LoadEngineSnapshotValues_RND()
16591 {
16592   unsigned long num_random_calls = game.num_random_calls;
16593   int i, j;
16594
16595   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16596   {
16597     int element = EL_CUSTOM_START + i;
16598
16599     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16600   }
16601
16602   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16603   {
16604     int element = EL_GROUP_START + i;
16605
16606     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16607   }
16608
16609   for (i = 0; i < 4; i++)
16610   {
16611     for (j = 0; j < NUM_BELT_PARTS; j++)
16612     {
16613       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16614       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16615
16616       graphic_info[graphic].anim_mode = anim_mode;
16617     }
16618   }
16619
16620   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16621   {
16622     InitRND(tape.random_seed);
16623     for (i = 0; i < num_random_calls; i++)
16624       RND(1);
16625   }
16626
16627   if (game.num_random_calls != num_random_calls)
16628   {
16629     Error(ERR_INFO, "number of random calls out of sync");
16630     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16631     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16632     Error(ERR_EXIT, "this should not happen -- please debug");
16633   }
16634 }
16635
16636 void SaveEngineSnapshot()
16637 {
16638   /* do not save snapshots from editor */
16639   if (level_editor_test_game)
16640     return;
16641
16642   /* free previous snapshot buffers, if needed */
16643   FreeEngineSnapshotBuffers();
16644
16645   /* copy some special values to a structure better suited for the snapshot */
16646
16647   SaveEngineSnapshotValues_RND();
16648   SaveEngineSnapshotValues_EM();
16649   SaveEngineSnapshotValues_SP();
16650
16651   /* save values stored in special snapshot structure */
16652
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16655   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16656
16657   /* save further RND engine values */
16658
16659   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16660   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16661   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16662
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16665   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16666   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16667
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16672   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16673
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16677
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16679
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16681
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16684
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16694   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16703
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16706
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16710
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16713
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16719
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16722
16723   /* save level identification information */
16724
16725   setString(&snapshot_level_identifier, leveldir_current->identifier);
16726   snapshot_level_nr = level_nr;
16727
16728 #if 0
16729   ListNode *node = engine_snapshot_list_rnd;
16730   int num_bytes = 0;
16731
16732   while (node != NULL)
16733   {
16734     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16735
16736     node = node->next;
16737   }
16738
16739   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16740 #endif
16741 }
16742
16743 void LoadEngineSnapshot()
16744 {
16745   /* restore generically stored snapshot buffers */
16746
16747   LoadEngineSnapshotBuffers();
16748
16749   /* restore special values from snapshot structure */
16750
16751   LoadEngineSnapshotValues_RND();
16752   LoadEngineSnapshotValues_EM();
16753   LoadEngineSnapshotValues_SP();
16754 }
16755
16756 boolean CheckEngineSnapshot()
16757 {
16758   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16759           snapshot_level_nr == level_nr);
16760 }
16761
16762
16763 /* ---------- new game button stuff ---------------------------------------- */
16764
16765 static struct
16766 {
16767   int graphic;
16768   struct Rect *pos;
16769   int gadget_id;
16770   char *infotext;
16771 } gamebutton_info[NUM_GAME_BUTTONS] =
16772 {
16773   {
16774     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16775     GAME_CTRL_ID_STOP,                  "stop game"
16776   },
16777   {
16778     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16779     GAME_CTRL_ID_PAUSE,                 "pause game"
16780   },
16781   {
16782     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16783     GAME_CTRL_ID_PLAY,                  "play game"
16784   },
16785   {
16786     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16787     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16788   },
16789   {
16790     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16791     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16792   },
16793   {
16794     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16795     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16796   }
16797 };
16798
16799 void CreateGameButtons()
16800 {
16801   int i;
16802
16803   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16804   {
16805     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16806     struct Rect *pos = gamebutton_info[i].pos;
16807     struct GadgetInfo *gi;
16808     int button_type;
16809     boolean checked;
16810     unsigned long event_mask;
16811     int gd_x   = gfx->src_x;
16812     int gd_y   = gfx->src_y;
16813     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16814     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16815     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16816     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16817     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16818     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16819     int id = i;
16820
16821     if (id == GAME_CTRL_ID_STOP ||
16822         id == GAME_CTRL_ID_PAUSE ||
16823         id == GAME_CTRL_ID_PLAY)
16824     {
16825       button_type = GD_TYPE_NORMAL_BUTTON;
16826       checked = FALSE;
16827       event_mask = GD_EVENT_RELEASED;
16828     }
16829     else
16830     {
16831       button_type = GD_TYPE_CHECK_BUTTON;
16832       checked =
16833         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16834          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16835          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16836       event_mask = GD_EVENT_PRESSED;
16837     }
16838
16839     gi = CreateGadget(GDI_CUSTOM_ID, id,
16840                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16841                       GDI_X, DX + pos->x,
16842                       GDI_Y, DY + pos->y,
16843                       GDI_WIDTH, gfx->width,
16844                       GDI_HEIGHT, gfx->height,
16845                       GDI_TYPE, button_type,
16846                       GDI_STATE, GD_BUTTON_UNPRESSED,
16847                       GDI_CHECKED, checked,
16848                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16849                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16850                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16851                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16852                       GDI_DIRECT_DRAW, FALSE,
16853                       GDI_EVENT_MASK, event_mask,
16854                       GDI_CALLBACK_ACTION, HandleGameButtons,
16855                       GDI_END);
16856
16857     if (gi == NULL)
16858       Error(ERR_EXIT, "cannot create gadget");
16859
16860     game_gadget[id] = gi;
16861   }
16862 }
16863
16864 void FreeGameButtons()
16865 {
16866   int i;
16867
16868   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16869     FreeGadget(game_gadget[i]);
16870 }
16871
16872 static void MapGameButtons()
16873 {
16874   int i;
16875
16876   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16877     MapGadget(game_gadget[i]);
16878 }
16879
16880 void UnmapGameButtons()
16881 {
16882   int i;
16883
16884   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16885     UnmapGadget(game_gadget[i]);
16886 }
16887
16888 void RedrawGameButtons()
16889 {
16890   int i;
16891
16892   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16893     RedrawGadget(game_gadget[i]);
16894 }
16895
16896 static void HandleGameButtonsExt(int id)
16897 {
16898   if (game_status != GAME_MODE_PLAYING)
16899     return;
16900
16901   switch (id)
16902   {
16903     case GAME_CTRL_ID_STOP:
16904       if (tape.playing)
16905         TapeStop();
16906       else
16907         RequestQuitGame(TRUE);
16908       break;
16909
16910     case GAME_CTRL_ID_PAUSE:
16911       if (options.network)
16912       {
16913 #if defined(NETWORK_AVALIABLE)
16914         if (tape.pausing)
16915           SendToServer_ContinuePlaying();
16916         else
16917           SendToServer_PausePlaying();
16918 #endif
16919       }
16920       else
16921         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16922       break;
16923
16924     case GAME_CTRL_ID_PLAY:
16925       if (tape.pausing)
16926       {
16927 #if defined(NETWORK_AVALIABLE)
16928         if (options.network)
16929           SendToServer_ContinuePlaying();
16930         else
16931 #endif
16932         {
16933           tape.pausing = FALSE;
16934           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16935         }
16936       }
16937       break;
16938
16939     case SOUND_CTRL_ID_MUSIC:
16940       if (setup.sound_music)
16941       { 
16942         setup.sound_music = FALSE;
16943
16944         FadeMusic();
16945       }
16946       else if (audio.music_available)
16947       { 
16948         setup.sound = setup.sound_music = TRUE;
16949
16950         SetAudioMode(setup.sound);
16951
16952         PlayLevelMusic();
16953       }
16954       break;
16955
16956     case SOUND_CTRL_ID_LOOPS:
16957       if (setup.sound_loops)
16958         setup.sound_loops = FALSE;
16959       else if (audio.loops_available)
16960       {
16961         setup.sound = setup.sound_loops = TRUE;
16962
16963         SetAudioMode(setup.sound);
16964       }
16965       break;
16966
16967     case SOUND_CTRL_ID_SIMPLE:
16968       if (setup.sound_simple)
16969         setup.sound_simple = FALSE;
16970       else if (audio.sound_available)
16971       {
16972         setup.sound = setup.sound_simple = TRUE;
16973
16974         SetAudioMode(setup.sound);
16975       }
16976       break;
16977
16978     default:
16979       break;
16980   }
16981 }
16982
16983 static void HandleGameButtons(struct GadgetInfo *gi)
16984 {
16985   HandleGameButtonsExt(gi->custom_id);
16986 }
16987
16988 void HandleSoundButtonKeys(Key key)
16989 {
16990 #if 1
16991   if (key == setup.shortcut.sound_simple)
16992     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16993   else if (key == setup.shortcut.sound_loops)
16994     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16995   else if (key == setup.shortcut.sound_music)
16996     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16997 #else
16998   if (key == setup.shortcut.sound_simple)
16999     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17000   else if (key == setup.shortcut.sound_loops)
17001     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17002   else if (key == setup.shortcut.sound_music)
17003     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17004 #endif
17005 }