rnd-20121013-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 #if 1
2227   int time = (local_player->LevelSolved ?
2228               local_player->LevelSolved_CountingTime :
2229               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230               level.native_em_level->lev->time :
2231               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232               level.native_sp_level->game_sp->time_played :
2233               game.no_time_limit ? TimePlayed : TimeLeft);
2234 #else
2235   int time = (local_player->LevelSolved ?
2236               local_player->LevelSolved_CountingTime :
2237               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238               level.native_em_level->lev->time :
2239               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240               level.native_sp_level->game_sp->time_played :
2241               level.time == 0 ? TimePlayed : TimeLeft);
2242 #endif
2243   int score = (local_player->LevelSolved ?
2244                local_player->LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                level.native_em_level->lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                level.native_sp_level->game_sp->score :
2249                local_player->score);
2250   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2251               level.native_em_level->lev->required :
2252               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2253               level.native_sp_level->game_sp->infotrons_still_needed :
2254               local_player->gems_still_needed);
2255   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2256                      level.native_em_level->lev->required > 0 :
2257                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2258                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2259                      local_player->gems_still_needed > 0 ||
2260                      local_player->sokobanfields_still_needed > 0 ||
2261                      local_player->lights_still_needed > 0);
2262
2263   UpdatePlayfieldElementCount();
2264
2265   /* update game panel control values */
2266
2267   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2268   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2269
2270   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2271   for (i = 0; i < MAX_NUM_KEYS; i++)
2272     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2273   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2274   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2275
2276   if (game.centered_player_nr == -1)
2277   {
2278     for (i = 0; i < MAX_PLAYERS; i++)
2279     {
2280       /* only one player in Supaplex game engine */
2281       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2282         break;
2283
2284       for (k = 0; k < MAX_NUM_KEYS; k++)
2285       {
2286         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287         {
2288           if (level.native_em_level->ply[i]->keys & (1 << k))
2289             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290               get_key_element_from_nr(k);
2291         }
2292         else if (stored_player[i].key[k])
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296
2297       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299           level.native_em_level->ply[i]->dynamite;
2300       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2301         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302           level.native_sp_level->game_sp->red_disk_count;
2303       else
2304         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305           stored_player[i].inventory_size;
2306
2307       if (stored_player[i].num_white_keys > 0)
2308         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2309           EL_DC_KEY_WHITE;
2310
2311       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312         stored_player[i].num_white_keys;
2313     }
2314   }
2315   else
2316   {
2317     int player_nr = game.centered_player_nr;
2318
2319     for (k = 0; k < MAX_NUM_KEYS; k++)
2320     {
2321       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2322       {
2323         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2324           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325             get_key_element_from_nr(k);
2326       }
2327       else if (stored_player[player_nr].key[k])
2328         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329           get_key_element_from_nr(k);
2330     }
2331
2332     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2333       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2334         level.native_em_level->ply[player_nr]->dynamite;
2335     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2336       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2337         level.native_sp_level->game_sp->red_disk_count;
2338     else
2339       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2340         stored_player[player_nr].inventory_size;
2341
2342     if (stored_player[player_nr].num_white_keys > 0)
2343       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2344
2345     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2346       stored_player[player_nr].num_white_keys;
2347   }
2348
2349   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2350   {
2351     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2352       get_inventory_element_from_pos(local_player, i);
2353     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2354       get_inventory_element_from_pos(local_player, -i - 1);
2355   }
2356
2357   game_panel_controls[GAME_PANEL_SCORE].value = score;
2358   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2359
2360   game_panel_controls[GAME_PANEL_TIME].value = time;
2361
2362   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2363   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2364   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2365
2366   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2367
2368   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2369     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2370      EL_EMPTY);
2371   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2372     local_player->shield_normal_time_left;
2373   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2374     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2375      EL_EMPTY);
2376   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2377     local_player->shield_deadly_time_left;
2378
2379   game_panel_controls[GAME_PANEL_EXIT].value =
2380     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2381
2382   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2383     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2384   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2385     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2386      EL_EMC_MAGIC_BALL_SWITCH);
2387
2388   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2389     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2390   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2391     game.light_time_left;
2392
2393   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2394     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2395   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2396     game.timegate_time_left;
2397
2398   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2399     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2400
2401   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2402     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2403   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2404     game.lenses_time_left;
2405
2406   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2407     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2408   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2409     game.magnify_time_left;
2410
2411   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2412     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2413      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2414      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2415      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2416      EL_BALLOON_SWITCH_NONE);
2417
2418   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2419     local_player->dynabomb_count;
2420   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2421     local_player->dynabomb_size;
2422   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2423     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2424
2425   game_panel_controls[GAME_PANEL_PENGUINS].value =
2426     local_player->friends_still_needed;
2427
2428   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2429     local_player->sokobanfields_still_needed;
2430   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2431     local_player->sokobanfields_still_needed;
2432
2433   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2434     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2435
2436   for (i = 0; i < NUM_BELTS; i++)
2437   {
2438     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2439       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2440        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2441     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2442       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2443   }
2444
2445   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2446     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2447   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2448     game.magic_wall_time_left;
2449
2450 #if USE_PLAYER_GRAVITY
2451   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2452     local_player->gravity;
2453 #else
2454   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2455 #endif
2456
2457   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2458     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2459
2460   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2461     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2462       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2463        game.panel.element[i].id : EL_UNDEFINED);
2464
2465   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2466     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2467       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2468        element_info[game.panel.element_count[i].id].element_count : 0);
2469
2470   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2471     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2472       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2473        element_info[game.panel.ce_score[i].id].collect_score : 0);
2474
2475   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2476     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2477       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2478        element_info[game.panel.ce_score_element[i].id].collect_score :
2479        EL_UNDEFINED);
2480
2481   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2482   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2483   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2484
2485   /* update game panel control frames */
2486
2487   for (i = 0; game_panel_controls[i].nr != -1; i++)
2488   {
2489     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2490
2491     if (gpc->type == TYPE_ELEMENT)
2492     {
2493       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2494       {
2495         int last_anim_random_frame = gfx.anim_random_frame;
2496         int element = gpc->value;
2497         int graphic = el2panelimg(element);
2498
2499         if (gpc->value != gpc->last_value)
2500         {
2501           gpc->gfx_frame = 0;
2502           gpc->gfx_random = INIT_GFX_RANDOM();
2503         }
2504         else
2505         {
2506           gpc->gfx_frame++;
2507
2508           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2509               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2510             gpc->gfx_random = INIT_GFX_RANDOM();
2511         }
2512
2513         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2514           gfx.anim_random_frame = gpc->gfx_random;
2515
2516         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2517           gpc->gfx_frame = element_info[element].collect_score;
2518
2519         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2520                                               gpc->gfx_frame);
2521
2522         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2523           gfx.anim_random_frame = last_anim_random_frame;
2524       }
2525     }
2526   }
2527 }
2528
2529 void DisplayGameControlValues()
2530 {
2531   boolean redraw_panel = FALSE;
2532   int i;
2533
2534   for (i = 0; game_panel_controls[i].nr != -1; i++)
2535   {
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2537
2538     if (PANEL_DEACTIVATED(gpc->pos))
2539       continue;
2540
2541     if (gpc->value == gpc->last_value &&
2542         gpc->frame == gpc->last_frame)
2543       continue;
2544
2545     redraw_panel = TRUE;
2546   }
2547
2548   if (!redraw_panel)
2549     return;
2550
2551   /* copy default game door content to main double buffer */
2552 #if 1
2553   /* !!! CHECK AGAIN !!! */
2554   SetPanelBackground();
2555   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2556   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2557 #else
2558   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2559              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2560 #endif
2561
2562   /* redraw game control buttons */
2563 #if 1
2564   RedrawGameButtons();
2565 #else
2566   UnmapGameButtons();
2567   MapGameButtons();
2568 #endif
2569
2570   game_status = GAME_MODE_PSEUDO_PANEL;
2571
2572 #if 1
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574 #else
2575   for (i = 0; game_panel_controls[i].nr != -1; i++)
2576 #endif
2577   {
2578 #if 1
2579     int nr = game_panel_order[i].nr;
2580     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2581 #else
2582     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2583     int nr = gpc->nr;
2584 #endif
2585     struct TextPosInfo *pos = gpc->pos;
2586     int type = gpc->type;
2587     int value = gpc->value;
2588     int frame = gpc->frame;
2589 #if 0
2590     int last_value = gpc->last_value;
2591     int last_frame = gpc->last_frame;
2592 #endif
2593     int size = pos->size;
2594     int font = pos->font;
2595     boolean draw_masked = pos->draw_masked;
2596     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2597
2598     if (PANEL_DEACTIVATED(pos))
2599       continue;
2600
2601 #if 0
2602     if (value == last_value && frame == last_frame)
2603       continue;
2604 #endif
2605
2606     gpc->last_value = value;
2607     gpc->last_frame = frame;
2608
2609 #if 0
2610     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2611 #endif
2612
2613     if (type == TYPE_INTEGER)
2614     {
2615       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2616           nr == GAME_PANEL_TIME)
2617       {
2618         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2619
2620         if (use_dynamic_size)           /* use dynamic number of digits */
2621         {
2622           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2623           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2624           int size2 = size1 + 1;
2625           int font1 = pos->font;
2626           int font2 = pos->font_alt;
2627
2628           size = (value < value_change ? size1 : size2);
2629           font = (value < value_change ? font1 : font2);
2630
2631 #if 0
2632           /* clear background if value just changed its size (dynamic digits) */
2633           if ((last_value < value_change) != (value < value_change))
2634           {
2635             int width1 = size1 * getFontWidth(font1);
2636             int width2 = size2 * getFontWidth(font2);
2637             int max_width = MAX(width1, width2);
2638             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2639
2640             pos->width = max_width;
2641
2642             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2643                                        max_width, max_height);
2644           }
2645 #endif
2646         }
2647       }
2648
2649 #if 1
2650       /* correct text size if "digits" is zero or less */
2651       if (size <= 0)
2652         size = strlen(int2str(value, size));
2653
2654       /* dynamically correct text alignment */
2655       pos->width = size * getFontWidth(font);
2656 #endif
2657
2658       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2659                   int2str(value, size), font, mask_mode);
2660     }
2661     else if (type == TYPE_ELEMENT)
2662     {
2663       int element, graphic;
2664       Bitmap *src_bitmap;
2665       int src_x, src_y;
2666       int width, height;
2667       int dst_x = PANEL_XPOS(pos);
2668       int dst_y = PANEL_YPOS(pos);
2669
2670 #if 1
2671       if (value != EL_UNDEFINED && value != EL_EMPTY)
2672       {
2673         element = value;
2674         graphic = el2panelimg(value);
2675
2676         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2677
2678 #if 1
2679         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2680           size = TILESIZE;
2681 #endif
2682
2683         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2684                               &src_x, &src_y);
2685
2686         width  = graphic_info[graphic].width  * size / TILESIZE;
2687         height = graphic_info[graphic].height * size / TILESIZE;
2688
2689         if (draw_masked)
2690         {
2691           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2692                         dst_x - src_x, dst_y - src_y);
2693           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2694                            dst_x, dst_y);
2695         }
2696         else
2697         {
2698           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2699                      dst_x, dst_y);
2700         }
2701       }
2702 #else
2703       if (value == EL_UNDEFINED || value == EL_EMPTY)
2704       {
2705         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2706         graphic = el2panelimg(element);
2707
2708         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2709         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2710         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2711       }
2712       else
2713       {
2714         element = value;
2715         graphic = el2panelimg(value);
2716
2717         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2718       }
2719
2720       width  = graphic_info[graphic].width  * size / TILESIZE;
2721       height = graphic_info[graphic].height * size / TILESIZE;
2722
2723       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2724 #endif
2725     }
2726     else if (type == TYPE_STRING)
2727     {
2728       boolean active = (value != 0);
2729       char *state_normal = "off";
2730       char *state_active = "on";
2731       char *state = (active ? state_active : state_normal);
2732       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2733                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2734                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2735                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2736
2737       if (nr == GAME_PANEL_GRAVITY_STATE)
2738       {
2739         int font1 = pos->font;          /* (used for normal state) */
2740         int font2 = pos->font_alt;      /* (used for active state) */
2741 #if 0
2742         int size1 = strlen(state_normal);
2743         int size2 = strlen(state_active);
2744         int width1 = size1 * getFontWidth(font1);
2745         int width2 = size2 * getFontWidth(font2);
2746         int max_width = MAX(width1, width2);
2747         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2748
2749         pos->width = max_width;
2750
2751         /* clear background for values that may have changed its size */
2752         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2753                                    max_width, max_height);
2754 #endif
2755
2756         font = (active ? font2 : font1);
2757       }
2758
2759       if (s != NULL)
2760       {
2761         char *s_cut;
2762
2763 #if 1
2764         if (size <= 0)
2765         {
2766           /* don't truncate output if "chars" is zero or less */
2767           size = strlen(s);
2768
2769           /* dynamically correct text alignment */
2770           pos->width = size * getFontWidth(font);
2771         }
2772 #endif
2773
2774         s_cut = getStringCopyN(s, size);
2775
2776         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2777                     s_cut, font, mask_mode);
2778
2779         free(s_cut);
2780       }
2781     }
2782
2783     redraw_mask |= REDRAW_DOOR_1;
2784   }
2785
2786   game_status = GAME_MODE_PLAYING;
2787 }
2788
2789 void UpdateAndDisplayGameControlValues()
2790 {
2791   if (tape.warp_forward)
2792     return;
2793
2794   UpdateGameControlValues();
2795   DisplayGameControlValues();
2796 }
2797
2798 void DrawGameValue_Emeralds(int value)
2799 {
2800   struct TextPosInfo *pos = &game.panel.gems;
2801 #if 1
2802   int font_nr = pos->font;
2803 #else
2804   int font_nr = FONT_TEXT_2;
2805 #endif
2806   int font_width = getFontWidth(font_nr);
2807   int chars = pos->size;
2808
2809 #if 1
2810   return;       /* !!! USE NEW STUFF !!! */
2811 #endif
2812
2813   if (PANEL_DEACTIVATED(pos))
2814     return;
2815
2816   pos->width = chars * font_width;
2817
2818   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2819 }
2820
2821 void DrawGameValue_Dynamite(int value)
2822 {
2823   struct TextPosInfo *pos = &game.panel.inventory_count;
2824 #if 1
2825   int font_nr = pos->font;
2826 #else
2827   int font_nr = FONT_TEXT_2;
2828 #endif
2829   int font_width = getFontWidth(font_nr);
2830   int chars = pos->size;
2831
2832 #if 1
2833   return;       /* !!! USE NEW STUFF !!! */
2834 #endif
2835
2836   if (PANEL_DEACTIVATED(pos))
2837     return;
2838
2839   pos->width = chars * font_width;
2840
2841   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2842 }
2843
2844 void DrawGameValue_Score(int value)
2845 {
2846   struct TextPosInfo *pos = &game.panel.score;
2847 #if 1
2848   int font_nr = pos->font;
2849 #else
2850   int font_nr = FONT_TEXT_2;
2851 #endif
2852   int font_width = getFontWidth(font_nr);
2853   int chars = pos->size;
2854
2855 #if 1
2856   return;       /* !!! USE NEW STUFF !!! */
2857 #endif
2858
2859   if (PANEL_DEACTIVATED(pos))
2860     return;
2861
2862   pos->width = chars * font_width;
2863
2864   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2865 }
2866
2867 void DrawGameValue_Time(int value)
2868 {
2869   struct TextPosInfo *pos = &game.panel.time;
2870   static int last_value = -1;
2871   int chars1 = 3;
2872   int chars2 = 4;
2873   int chars = pos->size;
2874 #if 1
2875   int font1_nr = pos->font;
2876   int font2_nr = pos->font_alt;
2877 #else
2878   int font1_nr = FONT_TEXT_2;
2879   int font2_nr = FONT_TEXT_1;
2880 #endif
2881   int font_nr = font1_nr;
2882   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2883
2884 #if 1
2885   return;       /* !!! USE NEW STUFF !!! */
2886 #endif
2887
2888   if (PANEL_DEACTIVATED(pos))
2889     return;
2890
2891   if (use_dynamic_chars)                /* use dynamic number of chars */
2892   {
2893     chars   = (value < 1000 ? chars1   : chars2);
2894     font_nr = (value < 1000 ? font1_nr : font2_nr);
2895   }
2896
2897   /* clear background if value just changed its size (dynamic chars only) */
2898   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2899   {
2900     int width1 = chars1 * getFontWidth(font1_nr);
2901     int width2 = chars2 * getFontWidth(font2_nr);
2902     int max_width = MAX(width1, width2);
2903     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2904
2905     pos->width = max_width;
2906
2907     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2908                                max_width, max_height);
2909   }
2910
2911   pos->width = chars * getFontWidth(font_nr);
2912
2913   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2914
2915   last_value = value;
2916 }
2917
2918 void DrawGameValue_Level(int value)
2919 {
2920   struct TextPosInfo *pos = &game.panel.level_number;
2921   int chars1 = 2;
2922   int chars2 = 3;
2923   int chars = pos->size;
2924 #if 1
2925   int font1_nr = pos->font;
2926   int font2_nr = pos->font_alt;
2927 #else
2928   int font1_nr = FONT_TEXT_2;
2929   int font2_nr = FONT_TEXT_1;
2930 #endif
2931   int font_nr = font1_nr;
2932   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2933
2934 #if 1
2935   return;       /* !!! USE NEW STUFF !!! */
2936 #endif
2937
2938   if (PANEL_DEACTIVATED(pos))
2939     return;
2940
2941   if (use_dynamic_chars)                /* use dynamic number of chars */
2942   {
2943     chars   = (level_nr < 100 ? chars1   : chars2);
2944     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2945   }
2946
2947   pos->width = chars * getFontWidth(font_nr);
2948
2949   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2950 }
2951
2952 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2953 {
2954 #if 0
2955   struct TextPosInfo *pos = &game.panel.keys;
2956 #endif
2957 #if 0
2958   int base_key_graphic = EL_KEY_1;
2959 #endif
2960   int i;
2961
2962 #if 1
2963   return;       /* !!! USE NEW STUFF !!! */
2964 #endif
2965
2966 #if 0
2967   if (PANEL_DEACTIVATED(pos))
2968     return;
2969 #endif
2970
2971 #if 0
2972   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2973     base_key_graphic = EL_EM_KEY_1;
2974 #endif
2975
2976 #if 0
2977   pos->width = 4 * MINI_TILEX;
2978 #endif
2979
2980 #if 1
2981   for (i = 0; i < MAX_NUM_KEYS; i++)
2982 #else
2983   /* currently only 4 of 8 possible keys are displayed */
2984   for (i = 0; i < STD_NUM_KEYS; i++)
2985 #endif
2986   {
2987 #if 1
2988     struct TextPosInfo *pos = &game.panel.key[i];
2989 #endif
2990     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2991     int src_y = DOOR_GFX_PAGEY1 + 123;
2992 #if 1
2993     int dst_x = PANEL_XPOS(pos);
2994     int dst_y = PANEL_YPOS(pos);
2995 #else
2996     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2997     int dst_y = PANEL_YPOS(pos);
2998 #endif
2999
3000 #if 1
3001     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
3002                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
3003                    EL_KEY_1) + i;
3004     int graphic = el2edimg(element);
3005 #endif
3006
3007 #if 1
3008     if (PANEL_DEACTIVATED(pos))
3009       continue;
3010 #endif
3011
3012 #if 0
3013     /* masked blit with tiles from half-size scaled bitmap does not work yet
3014        (no mask bitmap created for these sizes after loading and scaling) --
3015        solution: load without creating mask, scale, then create final mask */
3016
3017     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3018                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3019
3020     if (key[i])
3021     {
3022 #if 0
3023       int graphic = el2edimg(base_key_graphic + i);
3024 #endif
3025       Bitmap *src_bitmap;
3026       int src_x, src_y;
3027
3028       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3029
3030       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3031                     dst_x - src_x, dst_y - src_y);
3032       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3033                        dst_x, dst_y);
3034     }
3035 #else
3036 #if 1
3037     if (key[i])
3038       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3039     else
3040       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3041                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3042 #else
3043     if (key[i])
3044       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3045     else
3046       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3047                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3048 #endif
3049 #endif
3050   }
3051 }
3052
3053 #else
3054
3055 void DrawGameValue_Emeralds(int value)
3056 {
3057   int font_nr = FONT_TEXT_2;
3058   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3059
3060   if (PANEL_DEACTIVATED(game.panel.gems))
3061     return;
3062
3063   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3064 }
3065
3066 void DrawGameValue_Dynamite(int value)
3067 {
3068   int font_nr = FONT_TEXT_2;
3069   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3070
3071   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3072     return;
3073
3074   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3075 }
3076
3077 void DrawGameValue_Score(int value)
3078 {
3079   int font_nr = FONT_TEXT_2;
3080   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3081
3082   if (PANEL_DEACTIVATED(game.panel.score))
3083     return;
3084
3085   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3086 }
3087
3088 void DrawGameValue_Time(int value)
3089 {
3090   int font1_nr = FONT_TEXT_2;
3091 #if 1
3092   int font2_nr = FONT_TEXT_1;
3093 #else
3094   int font2_nr = FONT_LEVEL_NUMBER;
3095 #endif
3096   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3097   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3098
3099   if (PANEL_DEACTIVATED(game.panel.time))
3100     return;
3101
3102   /* clear background if value just changed its size */
3103   if (value == 999 || value == 1000)
3104     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3105
3106   if (value < 1000)
3107     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3108   else
3109     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3110 }
3111
3112 void DrawGameValue_Level(int value)
3113 {
3114   int font1_nr = FONT_TEXT_2;
3115 #if 1
3116   int font2_nr = FONT_TEXT_1;
3117 #else
3118   int font2_nr = FONT_LEVEL_NUMBER;
3119 #endif
3120
3121   if (PANEL_DEACTIVATED(game.panel.level))
3122     return;
3123
3124   if (level_nr < 100)
3125     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3126   else
3127     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3128 }
3129
3130 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3131 {
3132   int base_key_graphic = EL_KEY_1;
3133   int i;
3134
3135   if (PANEL_DEACTIVATED(game.panel.keys))
3136     return;
3137
3138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3139     base_key_graphic = EL_EM_KEY_1;
3140
3141   /* currently only 4 of 8 possible keys are displayed */
3142   for (i = 0; i < STD_NUM_KEYS; i++)
3143   {
3144     int x = XX_KEYS + i * MINI_TILEX;
3145     int y = YY_KEYS;
3146
3147     if (key[i])
3148       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3149     else
3150       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3151                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3152   }
3153 }
3154
3155 #endif
3156
3157 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3158                        int key_bits)
3159 {
3160   int key[MAX_NUM_KEYS];
3161   int i;
3162
3163   /* prevent EM engine from updating time/score values parallel to GameWon() */
3164   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3165       local_player->LevelSolved)
3166     return;
3167
3168   for (i = 0; i < MAX_NUM_KEYS; i++)
3169     key[i] = key_bits & (1 << i);
3170
3171   DrawGameValue_Level(level_nr);
3172
3173   DrawGameValue_Emeralds(emeralds);
3174   DrawGameValue_Dynamite(dynamite);
3175   DrawGameValue_Score(score);
3176   DrawGameValue_Time(time);
3177
3178   DrawGameValue_Keys(key);
3179 }
3180
3181 void UpdateGameDoorValues()
3182 {
3183   UpdateGameControlValues();
3184 }
3185
3186 void DrawGameDoorValues()
3187 {
3188   DisplayGameControlValues();
3189 }
3190
3191 void DrawGameDoorValues_OLD()
3192 {
3193   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
3194   int dynamite_value = 0;
3195   int score_value = (local_player->LevelSolved ? local_player->score_final :
3196                      local_player->score);
3197   int gems_value = local_player->gems_still_needed;
3198   int key_bits = 0;
3199   int i, j;
3200
3201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3202   {
3203     DrawGameDoorValues_EM();
3204
3205     return;
3206   }
3207
3208   if (game.centered_player_nr == -1)
3209   {
3210     for (i = 0; i < MAX_PLAYERS; i++)
3211     {
3212       for (j = 0; j < MAX_NUM_KEYS; j++)
3213         if (stored_player[i].key[j])
3214           key_bits |= (1 << j);
3215
3216       dynamite_value += stored_player[i].inventory_size;
3217     }
3218   }
3219   else
3220   {
3221     int player_nr = game.centered_player_nr;
3222
3223     for (i = 0; i < MAX_NUM_KEYS; i++)
3224       if (stored_player[player_nr].key[i])
3225         key_bits |= (1 << i);
3226
3227     dynamite_value = stored_player[player_nr].inventory_size;
3228   }
3229
3230   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3231                     key_bits);
3232 }
3233
3234
3235 /*
3236   =============================================================================
3237   InitGameEngine()
3238   -----------------------------------------------------------------------------
3239   initialize game engine due to level / tape version number
3240   =============================================================================
3241 */
3242
3243 static void InitGameEngine()
3244 {
3245   int i, j, k, l, x, y;
3246
3247   /* set game engine from tape file when re-playing, else from level file */
3248   game.engine_version = (tape.playing ? tape.engine_version :
3249                          level.game_version);
3250
3251   /* ---------------------------------------------------------------------- */
3252   /* set flags for bugs and changes according to active game engine version */
3253   /* ---------------------------------------------------------------------- */
3254
3255   /*
3256     Summary of bugfix/change:
3257     Fixed handling for custom elements that change when pushed by the player.
3258
3259     Fixed/changed in version:
3260     3.1.0
3261
3262     Description:
3263     Before 3.1.0, custom elements that "change when pushing" changed directly
3264     after the player started pushing them (until then handled in "DigField()").
3265     Since 3.1.0, these custom elements are not changed until the "pushing"
3266     move of the element is finished (now handled in "ContinueMoving()").
3267
3268     Affected levels/tapes:
3269     The first condition is generally needed for all levels/tapes before version
3270     3.1.0, which might use the old behaviour before it was changed; known tapes
3271     that are affected are some tapes from the level set "Walpurgis Gardens" by
3272     Jamie Cullen.
3273     The second condition is an exception from the above case and is needed for
3274     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3275     above (including some development versions of 3.1.0), but before it was
3276     known that this change would break tapes like the above and was fixed in
3277     3.1.1, so that the changed behaviour was active although the engine version
3278     while recording maybe was before 3.1.0. There is at least one tape that is
3279     affected by this exception, which is the tape for the one-level set "Bug
3280     Machine" by Juergen Bonhagen.
3281   */
3282
3283   game.use_change_when_pushing_bug =
3284     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3285      !(tape.playing &&
3286        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3287        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3288
3289   /*
3290     Summary of bugfix/change:
3291     Fixed handling for blocking the field the player leaves when moving.
3292
3293     Fixed/changed in version:
3294     3.1.1
3295
3296     Description:
3297     Before 3.1.1, when "block last field when moving" was enabled, the field
3298     the player is leaving when moving was blocked for the time of the move,
3299     and was directly unblocked afterwards. This resulted in the last field
3300     being blocked for exactly one less than the number of frames of one player
3301     move. Additionally, even when blocking was disabled, the last field was
3302     blocked for exactly one frame.
3303     Since 3.1.1, due to changes in player movement handling, the last field
3304     is not blocked at all when blocking is disabled. When blocking is enabled,
3305     the last field is blocked for exactly the number of frames of one player
3306     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3307     last field is blocked for exactly one more than the number of frames of
3308     one player move.
3309
3310     Affected levels/tapes:
3311     (!!! yet to be determined -- probably many !!!)
3312   */
3313
3314   game.use_block_last_field_bug =
3315     (game.engine_version < VERSION_IDENT(3,1,1,0));
3316
3317   /*
3318     Summary of bugfix/change:
3319     Changed behaviour of CE changes with multiple changes per single frame.
3320
3321     Fixed/changed in version:
3322     3.2.0-6
3323
3324     Description:
3325     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3326     This resulted in race conditions where CEs seem to behave strange in some
3327     situations (where triggered CE changes were just skipped because there was
3328     already a CE change on that tile in the playfield in that engine frame).
3329     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3330     (The number of changes per frame must be limited in any case, because else
3331     it is easily possible to define CE changes that would result in an infinite
3332     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3333     should be set large enough so that it would only be reached in cases where
3334     the corresponding CE change conditions run into a loop. Therefore, it seems
3335     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3336     maximal number of change pages for custom elements.)
3337
3338     Affected levels/tapes:
3339     Probably many.
3340   */
3341
3342 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3343   game.max_num_changes_per_frame = 1;
3344 #else
3345   game.max_num_changes_per_frame =
3346     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3347 #endif
3348
3349   /* ---------------------------------------------------------------------- */
3350
3351   /* default scan direction: scan playfield from top/left to bottom/right */
3352   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3353
3354   /* dynamically adjust element properties according to game engine version */
3355   InitElementPropertiesEngine(game.engine_version);
3356
3357 #if 0
3358   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3359   printf("          tape version == %06d [%s] [file: %06d]\n",
3360          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3361          tape.file_version);
3362   printf("       => game.engine_version == %06d\n", game.engine_version);
3363 #endif
3364
3365   /* ---------- initialize player's initial move delay --------------------- */
3366
3367   /* dynamically adjust player properties according to level information */
3368   for (i = 0; i < MAX_PLAYERS; i++)
3369     game.initial_move_delay_value[i] =
3370       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3371
3372   /* dynamically adjust player properties according to game engine version */
3373   for (i = 0; i < MAX_PLAYERS; i++)
3374     game.initial_move_delay[i] =
3375       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3376        game.initial_move_delay_value[i] : 0);
3377
3378   /* ---------- initialize player's initial push delay --------------------- */
3379
3380   /* dynamically adjust player properties according to game engine version */
3381   game.initial_push_delay_value =
3382     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3383
3384   /* ---------- initialize changing elements ------------------------------- */
3385
3386   /* initialize changing elements information */
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388   {
3389     struct ElementInfo *ei = &element_info[i];
3390
3391     /* this pointer might have been changed in the level editor */
3392     ei->change = &ei->change_page[0];
3393
3394     if (!IS_CUSTOM_ELEMENT(i))
3395     {
3396       ei->change->target_element = EL_EMPTY_SPACE;
3397       ei->change->delay_fixed = 0;
3398       ei->change->delay_random = 0;
3399       ei->change->delay_frames = 1;
3400     }
3401
3402     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3403     {
3404       ei->has_change_event[j] = FALSE;
3405
3406       ei->event_page_nr[j] = 0;
3407       ei->event_page[j] = &ei->change_page[0];
3408     }
3409   }
3410
3411   /* add changing elements from pre-defined list */
3412   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3413   {
3414     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3415     struct ElementInfo *ei = &element_info[ch_delay->element];
3416
3417     ei->change->target_element       = ch_delay->target_element;
3418     ei->change->delay_fixed          = ch_delay->change_delay;
3419
3420     ei->change->pre_change_function  = ch_delay->pre_change_function;
3421     ei->change->change_function      = ch_delay->change_function;
3422     ei->change->post_change_function = ch_delay->post_change_function;
3423
3424     ei->change->can_change = TRUE;
3425     ei->change->can_change_or_has_action = TRUE;
3426
3427     ei->has_change_event[CE_DELAY] = TRUE;
3428
3429     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3430     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3431   }
3432
3433   /* ---------- initialize internal run-time variables --------------------- */
3434
3435   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3436   {
3437     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3438
3439     for (j = 0; j < ei->num_change_pages; j++)
3440     {
3441       ei->change_page[j].can_change_or_has_action =
3442         (ei->change_page[j].can_change |
3443          ei->change_page[j].has_action);
3444     }
3445   }
3446
3447   /* add change events from custom element configuration */
3448   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3449   {
3450     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3451
3452     for (j = 0; j < ei->num_change_pages; j++)
3453     {
3454       if (!ei->change_page[j].can_change_or_has_action)
3455         continue;
3456
3457       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3458       {
3459         /* only add event page for the first page found with this event */
3460         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3461         {
3462           ei->has_change_event[k] = TRUE;
3463
3464           ei->event_page_nr[k] = j;
3465           ei->event_page[k] = &ei->change_page[j];
3466         }
3467       }
3468     }
3469   }
3470
3471 #if 1
3472   /* ---------- initialize reference elements in change conditions --------- */
3473
3474   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3475   {
3476     int element = EL_CUSTOM_START + i;
3477     struct ElementInfo *ei = &element_info[element];
3478
3479     for (j = 0; j < ei->num_change_pages; j++)
3480     {
3481       int trigger_element = ei->change_page[j].initial_trigger_element;
3482
3483       if (trigger_element >= EL_PREV_CE_8 &&
3484           trigger_element <= EL_NEXT_CE_8)
3485         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3486
3487       ei->change_page[j].trigger_element = trigger_element;
3488     }
3489   }
3490 #endif
3491
3492   /* ---------- initialize run-time trigger player and element ------------- */
3493
3494   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3495   {
3496     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3497
3498     for (j = 0; j < ei->num_change_pages; j++)
3499     {
3500       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3501       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3502       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3503       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3504       ei->change_page[j].actual_trigger_ce_value = 0;
3505       ei->change_page[j].actual_trigger_ce_score = 0;
3506     }
3507   }
3508
3509   /* ---------- initialize trigger events ---------------------------------- */
3510
3511   /* initialize trigger events information */
3512   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3513     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3514       trigger_events[i][j] = FALSE;
3515
3516   /* add trigger events from element change event properties */
3517   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3518   {
3519     struct ElementInfo *ei = &element_info[i];
3520
3521     for (j = 0; j < ei->num_change_pages; j++)
3522     {
3523       if (!ei->change_page[j].can_change_or_has_action)
3524         continue;
3525
3526       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3527       {
3528         int trigger_element = ei->change_page[j].trigger_element;
3529
3530         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3531         {
3532           if (ei->change_page[j].has_event[k])
3533           {
3534             if (IS_GROUP_ELEMENT(trigger_element))
3535             {
3536               struct ElementGroupInfo *group =
3537                 element_info[trigger_element].group;
3538
3539               for (l = 0; l < group->num_elements_resolved; l++)
3540                 trigger_events[group->element_resolved[l]][k] = TRUE;
3541             }
3542             else if (trigger_element == EL_ANY_ELEMENT)
3543               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3544                 trigger_events[l][k] = TRUE;
3545             else
3546               trigger_events[trigger_element][k] = TRUE;
3547           }
3548         }
3549       }
3550     }
3551   }
3552
3553   /* ---------- initialize push delay -------------------------------------- */
3554
3555   /* initialize push delay values to default */
3556   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3557   {
3558     if (!IS_CUSTOM_ELEMENT(i))
3559     {
3560       /* set default push delay values (corrected since version 3.0.7-1) */
3561       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3562       {
3563         element_info[i].push_delay_fixed = 2;
3564         element_info[i].push_delay_random = 8;
3565       }
3566       else
3567       {
3568         element_info[i].push_delay_fixed = 8;
3569         element_info[i].push_delay_random = 8;
3570       }
3571     }
3572   }
3573
3574   /* set push delay value for certain elements from pre-defined list */
3575   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3576   {
3577     int e = push_delay_list[i].element;
3578
3579     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3580     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3581   }
3582
3583   /* set push delay value for Supaplex elements for newer engine versions */
3584   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3585   {
3586     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3587     {
3588       if (IS_SP_ELEMENT(i))
3589       {
3590         /* set SP push delay to just enough to push under a falling zonk */
3591         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3592
3593         element_info[i].push_delay_fixed  = delay;
3594         element_info[i].push_delay_random = 0;
3595       }
3596     }
3597   }
3598
3599   /* ---------- initialize move stepsize ----------------------------------- */
3600
3601   /* initialize move stepsize values to default */
3602   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3603     if (!IS_CUSTOM_ELEMENT(i))
3604       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3605
3606   /* set move stepsize value for certain elements from pre-defined list */
3607   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3608   {
3609     int e = move_stepsize_list[i].element;
3610
3611     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3612   }
3613
3614   /* ---------- initialize collect score ----------------------------------- */
3615
3616   /* initialize collect score values for custom elements from initial value */
3617   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3618     if (IS_CUSTOM_ELEMENT(i))
3619       element_info[i].collect_score = element_info[i].collect_score_initial;
3620
3621   /* ---------- initialize collect count ----------------------------------- */
3622
3623   /* initialize collect count values for non-custom elements */
3624   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3625     if (!IS_CUSTOM_ELEMENT(i))
3626       element_info[i].collect_count_initial = 0;
3627
3628   /* add collect count values for all elements from pre-defined list */
3629   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3630     element_info[collect_count_list[i].element].collect_count_initial =
3631       collect_count_list[i].count;
3632
3633   /* ---------- initialize access direction -------------------------------- */
3634
3635   /* initialize access direction values to default (access from every side) */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637     if (!IS_CUSTOM_ELEMENT(i))
3638       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3639
3640   /* set access direction value for certain elements from pre-defined list */
3641   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3642     element_info[access_direction_list[i].element].access_direction =
3643       access_direction_list[i].direction;
3644
3645   /* ---------- initialize explosion content ------------------------------- */
3646   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3647   {
3648     if (IS_CUSTOM_ELEMENT(i))
3649       continue;
3650
3651     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3652     {
3653       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3654
3655       element_info[i].content.e[x][y] =
3656         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3657          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3658          i == EL_PLAYER_3 ? EL_EMERALD :
3659          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3660          i == EL_MOLE ? EL_EMERALD_RED :
3661          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3662          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3663          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3664          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3665          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3666          i == EL_WALL_EMERALD ? EL_EMERALD :
3667          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3668          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3669          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3670          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3671          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3672          i == EL_WALL_PEARL ? EL_PEARL :
3673          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3674          EL_EMPTY);
3675     }
3676   }
3677
3678   /* ---------- initialize recursion detection ------------------------------ */
3679   recursion_loop_depth = 0;
3680   recursion_loop_detected = FALSE;
3681   recursion_loop_element = EL_UNDEFINED;
3682
3683   /* ---------- initialize graphics engine ---------------------------------- */
3684   game.scroll_delay_value =
3685     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3686      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3687   game.scroll_delay_value =
3688     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3689 }
3690
3691 int get_num_special_action(int element, int action_first, int action_last)
3692 {
3693   int num_special_action = 0;
3694   int i, j;
3695
3696   for (i = action_first; i <= action_last; i++)
3697   {
3698     boolean found = FALSE;
3699
3700     for (j = 0; j < NUM_DIRECTIONS; j++)
3701       if (el_act_dir2img(element, i, j) !=
3702           el_act_dir2img(element, ACTION_DEFAULT, j))
3703         found = TRUE;
3704
3705     if (found)
3706       num_special_action++;
3707     else
3708       break;
3709   }
3710
3711   return num_special_action;
3712 }
3713
3714
3715 /*
3716   =============================================================================
3717   InitGame()
3718   -----------------------------------------------------------------------------
3719   initialize and start new game
3720   =============================================================================
3721 */
3722
3723 void InitGame()
3724 {
3725   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3726   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3727   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3728 #if 0
3729   boolean do_fading = (game_status == GAME_MODE_MAIN);
3730 #endif
3731 #if 1
3732   int initial_move_dir = MV_DOWN;
3733 #else
3734   int initial_move_dir = MV_NONE;
3735 #endif
3736   int i, j, x, y;
3737
3738   game_status = GAME_MODE_PLAYING;
3739
3740 #if 1
3741   /* needed if different viewport properties defined for playing */
3742   ChangeViewportPropertiesIfNeeded();
3743 #endif
3744
3745 #if 1
3746   DrawCompleteVideoDisplay();
3747 #endif
3748
3749   InitGameEngine();
3750   InitGameControlValues();
3751
3752   /* don't play tapes over network */
3753   network_playing = (options.network && !tape.playing);
3754
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     struct PlayerInfo *player = &stored_player[i];
3758
3759     player->index_nr = i;
3760     player->index_bit = (1 << i);
3761     player->element_nr = EL_PLAYER_1 + i;
3762
3763     player->present = FALSE;
3764     player->active = FALSE;
3765     player->mapped = FALSE;
3766
3767     player->killed = FALSE;
3768     player->reanimated = FALSE;
3769
3770     player->action = 0;
3771     player->effective_action = 0;
3772     player->programmed_action = 0;
3773
3774     player->score = 0;
3775     player->score_final = 0;
3776
3777     player->gems_still_needed = level.gems_needed;
3778     player->sokobanfields_still_needed = 0;
3779     player->lights_still_needed = 0;
3780     player->friends_still_needed = 0;
3781
3782     for (j = 0; j < MAX_NUM_KEYS; j++)
3783       player->key[j] = FALSE;
3784
3785     player->num_white_keys = 0;
3786
3787     player->dynabomb_count = 0;
3788     player->dynabomb_size = 1;
3789     player->dynabombs_left = 0;
3790     player->dynabomb_xl = FALSE;
3791
3792     player->MovDir = initial_move_dir;
3793     player->MovPos = 0;
3794     player->GfxPos = 0;
3795     player->GfxDir = initial_move_dir;
3796     player->GfxAction = ACTION_DEFAULT;
3797     player->Frame = 0;
3798     player->StepFrame = 0;
3799
3800     player->initial_element = player->element_nr;
3801     player->artwork_element =
3802       (level.use_artwork_element[i] ? level.artwork_element[i] :
3803        player->element_nr);
3804     player->use_murphy = FALSE;
3805
3806     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3807     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3808
3809     player->gravity = level.initial_player_gravity[i];
3810
3811     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3812
3813     player->actual_frame_counter = 0;
3814
3815     player->step_counter = 0;
3816
3817     player->last_move_dir = initial_move_dir;
3818
3819     player->is_active = FALSE;
3820
3821     player->is_waiting = FALSE;
3822     player->is_moving = FALSE;
3823     player->is_auto_moving = FALSE;
3824     player->is_digging = FALSE;
3825     player->is_snapping = FALSE;
3826     player->is_collecting = FALSE;
3827     player->is_pushing = FALSE;
3828     player->is_switching = FALSE;
3829     player->is_dropping = FALSE;
3830     player->is_dropping_pressed = FALSE;
3831
3832     player->is_bored = FALSE;
3833     player->is_sleeping = FALSE;
3834
3835     player->frame_counter_bored = -1;
3836     player->frame_counter_sleeping = -1;
3837
3838     player->anim_delay_counter = 0;
3839     player->post_delay_counter = 0;
3840
3841     player->dir_waiting = initial_move_dir;
3842     player->action_waiting = ACTION_DEFAULT;
3843     player->last_action_waiting = ACTION_DEFAULT;
3844     player->special_action_bored = ACTION_DEFAULT;
3845     player->special_action_sleeping = ACTION_DEFAULT;
3846
3847     player->switch_x = -1;
3848     player->switch_y = -1;
3849
3850     player->drop_x = -1;
3851     player->drop_y = -1;
3852
3853     player->show_envelope = 0;
3854
3855     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3856
3857     player->push_delay       = -1;      /* initialized when pushing starts */
3858     player->push_delay_value = game.initial_push_delay_value;
3859
3860     player->drop_delay = 0;
3861     player->drop_pressed_delay = 0;
3862
3863     player->last_jx = -1;
3864     player->last_jy = -1;
3865     player->jx = -1;
3866     player->jy = -1;
3867
3868     player->shield_normal_time_left = 0;
3869     player->shield_deadly_time_left = 0;
3870
3871     player->inventory_infinite_element = EL_UNDEFINED;
3872     player->inventory_size = 0;
3873
3874     if (level.use_initial_inventory[i])
3875     {
3876       for (j = 0; j < level.initial_inventory_size[i]; j++)
3877       {
3878         int element = level.initial_inventory_content[i][j];
3879         int collect_count = element_info[element].collect_count_initial;
3880         int k;
3881
3882         if (!IS_CUSTOM_ELEMENT(element))
3883           collect_count = 1;
3884
3885         if (collect_count == 0)
3886           player->inventory_infinite_element = element;
3887         else
3888           for (k = 0; k < collect_count; k++)
3889             if (player->inventory_size < MAX_INVENTORY_SIZE)
3890               player->inventory_element[player->inventory_size++] = element;
3891       }
3892     }
3893
3894     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3895     SnapField(player, 0, 0);
3896
3897     player->LevelSolved = FALSE;
3898     player->GameOver = FALSE;
3899
3900     player->LevelSolved_GameWon = FALSE;
3901     player->LevelSolved_GameEnd = FALSE;
3902     player->LevelSolved_PanelOff = FALSE;
3903     player->LevelSolved_SaveTape = FALSE;
3904     player->LevelSolved_SaveScore = FALSE;
3905     player->LevelSolved_CountingTime = 0;
3906     player->LevelSolved_CountingScore = 0;
3907
3908     map_player_action[i] = i;
3909   }
3910
3911   network_player_action_received = FALSE;
3912
3913 #if defined(NETWORK_AVALIABLE)
3914   /* initial null action */
3915   if (network_playing)
3916     SendToServer_MovePlayer(MV_NONE);
3917 #endif
3918
3919   ZX = ZY = -1;
3920   ExitX = ExitY = -1;
3921
3922   FrameCounter = 0;
3923   TimeFrames = 0;
3924   TimePlayed = 0;
3925   TimeLeft = level.time;
3926   TapeTime = 0;
3927
3928   ScreenMovDir = MV_NONE;
3929   ScreenMovPos = 0;
3930   ScreenGfxPos = 0;
3931
3932   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3933
3934   AllPlayersGone = FALSE;
3935
3936   game.no_time_limit = (level.time == 0);
3937
3938   game.yamyam_content_nr = 0;
3939   game.robot_wheel_active = FALSE;
3940   game.magic_wall_active = FALSE;
3941   game.magic_wall_time_left = 0;
3942   game.light_time_left = 0;
3943   game.timegate_time_left = 0;
3944   game.switchgate_pos = 0;
3945   game.wind_direction = level.wind_direction_initial;
3946
3947 #if !USE_PLAYER_GRAVITY
3948   game.gravity = FALSE;
3949   game.explosions_delayed = TRUE;
3950 #endif
3951
3952   game.lenses_time_left = 0;
3953   game.magnify_time_left = 0;
3954
3955   game.ball_state = level.ball_state_initial;
3956   game.ball_content_nr = 0;
3957
3958   game.envelope_active = FALSE;
3959
3960   /* set focus to local player for network games, else to all players */
3961   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3962   game.centered_player_nr_next = game.centered_player_nr;
3963   game.set_centered_player = FALSE;
3964
3965   if (network_playing && tape.recording)
3966   {
3967     /* store client dependent player focus when recording network games */
3968     tape.centered_player_nr_next = game.centered_player_nr_next;
3969     tape.set_centered_player = TRUE;
3970   }
3971
3972   for (i = 0; i < NUM_BELTS; i++)
3973   {
3974     game.belt_dir[i] = MV_NONE;
3975     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3976   }
3977
3978   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3979     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3980
3981   SCAN_PLAYFIELD(x, y)
3982   {
3983     Feld[x][y] = level.field[x][y];
3984     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3985     ChangeDelay[x][y] = 0;
3986     ChangePage[x][y] = -1;
3987 #if USE_NEW_CUSTOM_VALUE
3988     CustomValue[x][y] = 0;              /* initialized in InitField() */
3989 #endif
3990     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3991     AmoebaNr[x][y] = 0;
3992     WasJustMoving[x][y] = 0;
3993     WasJustFalling[x][y] = 0;
3994     CheckCollision[x][y] = 0;
3995     CheckImpact[x][y] = 0;
3996     Stop[x][y] = FALSE;
3997     Pushed[x][y] = FALSE;
3998
3999     ChangeCount[x][y] = 0;
4000     ChangeEvent[x][y] = -1;
4001
4002     ExplodePhase[x][y] = 0;
4003     ExplodeDelay[x][y] = 0;
4004     ExplodeField[x][y] = EX_TYPE_NONE;
4005
4006     RunnerVisit[x][y] = 0;
4007     PlayerVisit[x][y] = 0;
4008
4009     GfxFrame[x][y] = 0;
4010     GfxRandom[x][y] = INIT_GFX_RANDOM();
4011     GfxElement[x][y] = EL_UNDEFINED;
4012     GfxAction[x][y] = ACTION_DEFAULT;
4013     GfxDir[x][y] = MV_NONE;
4014     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4015   }
4016
4017   SCAN_PLAYFIELD(x, y)
4018   {
4019     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4020       emulate_bd = FALSE;
4021     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4022       emulate_sb = FALSE;
4023     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4024       emulate_sp = FALSE;
4025
4026     InitField(x, y, TRUE);
4027
4028     ResetGfxAnimation(x, y);
4029   }
4030
4031   InitBeltMovement();
4032
4033   for (i = 0; i < MAX_PLAYERS; i++)
4034   {
4035     struct PlayerInfo *player = &stored_player[i];
4036
4037     /* set number of special actions for bored and sleeping animation */
4038     player->num_special_action_bored =
4039       get_num_special_action(player->artwork_element,
4040                              ACTION_BORING_1, ACTION_BORING_LAST);
4041     player->num_special_action_sleeping =
4042       get_num_special_action(player->artwork_element,
4043                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4044   }
4045
4046   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4047                     emulate_sb ? EMU_SOKOBAN :
4048                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4049
4050 #if USE_NEW_ALL_SLIPPERY
4051   /* initialize type of slippery elements */
4052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4053   {
4054     if (!IS_CUSTOM_ELEMENT(i))
4055     {
4056       /* default: elements slip down either to the left or right randomly */
4057       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4058
4059       /* SP style elements prefer to slip down on the left side */
4060       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4061         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4062
4063       /* BD style elements prefer to slip down on the left side */
4064       if (game.emulation == EMU_BOULDERDASH)
4065         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4066     }
4067   }
4068 #endif
4069
4070   /* initialize explosion and ignition delay */
4071   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4072   {
4073     if (!IS_CUSTOM_ELEMENT(i))
4074     {
4075       int num_phase = 8;
4076       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4077                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4078                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4079       int last_phase = (num_phase + 1) * delay;
4080       int half_phase = (num_phase / 2) * delay;
4081
4082       element_info[i].explosion_delay = last_phase - 1;
4083       element_info[i].ignition_delay = half_phase;
4084
4085       if (i == EL_BLACK_ORB)
4086         element_info[i].ignition_delay = 1;
4087     }
4088
4089 #if 0
4090     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4091       element_info[i].explosion_delay = 1;
4092
4093     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4094       element_info[i].ignition_delay = 1;
4095 #endif
4096   }
4097
4098   /* correct non-moving belts to start moving left */
4099   for (i = 0; i < NUM_BELTS; i++)
4100     if (game.belt_dir[i] == MV_NONE)
4101       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4102
4103 #if USE_NEW_PLAYER_ASSIGNMENTS
4104   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4105   /* choose default local player */
4106   local_player = &stored_player[0];
4107
4108   for (i = 0; i < MAX_PLAYERS; i++)
4109     stored_player[i].connected = FALSE;
4110
4111   local_player->connected = TRUE;
4112   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4113
4114   if (tape.playing)
4115   {
4116     /* try to guess locally connected team mode players (needed for correct
4117        assignment of player figures from level to locally playing players) */
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120       if (tape.player_participates[i])
4121         stored_player[i].connected = TRUE;
4122   }
4123   else if (setup.team_mode && !options.network)
4124   {
4125     /* try to guess locally connected team mode players (needed for correct
4126        assignment of player figures from level to locally playing players) */
4127
4128     for (i = 0; i < MAX_PLAYERS; i++)
4129       if (setup.input[i].use_joystick ||
4130           setup.input[i].key.left != KSYM_UNDEFINED)
4131         stored_player[i].connected = TRUE;
4132   }
4133
4134 #if 0
4135   for (i = 0; i < MAX_PLAYERS; i++)
4136     printf("::: player %d: %s\n", i,
4137            (stored_player[i].connected ? "connected" : "not connected"));
4138
4139   for (i = 0; i < MAX_PLAYERS; i++)
4140     printf("::: player %d: %s\n", i,
4141            (stored_player[i].present ? "present" : "not present"));
4142 #endif
4143
4144   /* check if any connected player was not found in playfield */
4145   for (i = 0; i < MAX_PLAYERS; i++)
4146   {
4147     struct PlayerInfo *player = &stored_player[i];
4148
4149     if (player->connected && !player->present)
4150     {
4151       struct PlayerInfo *field_player = NULL;
4152
4153 #if 0
4154       printf("::: looking for field player for player %d ...\n", i);
4155 #endif
4156
4157       /* assign first free player found that is present in the playfield */
4158
4159       /* first try: look for unmapped playfield player that is not connected */
4160       if (field_player == NULL)
4161         for (j = 0; j < MAX_PLAYERS; j++)
4162           if (stored_player[j].present &&
4163               !stored_player[j].mapped &&
4164               !stored_player[j].connected)
4165             field_player = &stored_player[j];
4166
4167       /* second try: look for *any* unmapped playfield player */
4168       if (field_player == NULL)
4169         for (j = 0; j < MAX_PLAYERS; j++)
4170           if (stored_player[j].present &&
4171               !stored_player[j].mapped)
4172             field_player = &stored_player[j];
4173
4174       if (field_player != NULL)
4175       {
4176         int jx = field_player->jx, jy = field_player->jy;
4177
4178 #if 0
4179         printf("::: found player figure %d\n", field_player->index_nr);
4180 #endif
4181
4182         player->present = FALSE;
4183         player->active = FALSE;
4184
4185         field_player->present = TRUE;
4186         field_player->active = TRUE;
4187
4188         /*
4189         player->initial_element = field_player->initial_element;
4190         player->artwork_element = field_player->artwork_element;
4191
4192         player->block_last_field       = field_player->block_last_field;
4193         player->block_delay_adjustment = field_player->block_delay_adjustment;
4194         */
4195
4196         StorePlayer[jx][jy] = field_player->element_nr;
4197
4198         field_player->jx = field_player->last_jx = jx;
4199         field_player->jy = field_player->last_jy = jy;
4200
4201         if (local_player == player)
4202           local_player = field_player;
4203
4204         map_player_action[field_player->index_nr] = i;
4205
4206         field_player->mapped = TRUE;
4207
4208 #if 0
4209         printf("::: map_player_action[%d] == %d\n",
4210                field_player->index_nr, i);
4211 #endif
4212       }
4213     }
4214
4215     if (player->connected && player->present)
4216       player->mapped = TRUE;
4217   }
4218
4219 #else
4220
4221   /* check if any connected player was not found in playfield */
4222   for (i = 0; i < MAX_PLAYERS; i++)
4223   {
4224     struct PlayerInfo *player = &stored_player[i];
4225
4226     if (player->connected && !player->present)
4227     {
4228       for (j = 0; j < MAX_PLAYERS; j++)
4229       {
4230         struct PlayerInfo *field_player = &stored_player[j];
4231         int jx = field_player->jx, jy = field_player->jy;
4232
4233         /* assign first free player found that is present in the playfield */
4234         if (field_player->present && !field_player->connected)
4235         {
4236           player->present = TRUE;
4237           player->active = TRUE;
4238
4239           field_player->present = FALSE;
4240           field_player->active = FALSE;
4241
4242           player->initial_element = field_player->initial_element;
4243           player->artwork_element = field_player->artwork_element;
4244
4245           player->block_last_field       = field_player->block_last_field;
4246           player->block_delay_adjustment = field_player->block_delay_adjustment;
4247
4248           StorePlayer[jx][jy] = player->element_nr;
4249
4250           player->jx = player->last_jx = jx;
4251           player->jy = player->last_jy = jy;
4252
4253           break;
4254         }
4255       }
4256     }
4257   }
4258 #endif
4259
4260 #if 0
4261   printf("::: local_player->present == %d\n", local_player->present);
4262 #endif
4263
4264   if (tape.playing)
4265   {
4266     /* when playing a tape, eliminate all players who do not participate */
4267
4268 #if USE_NEW_PLAYER_ASSIGNMENTS
4269     for (i = 0; i < MAX_PLAYERS; i++)
4270     {
4271       if (stored_player[i].active &&
4272           !tape.player_participates[map_player_action[i]])
4273       {
4274         struct PlayerInfo *player = &stored_player[i];
4275         int jx = player->jx, jy = player->jy;
4276
4277         player->active = FALSE;
4278         StorePlayer[jx][jy] = 0;
4279         Feld[jx][jy] = EL_EMPTY;
4280       }
4281     }
4282 #else
4283     for (i = 0; i < MAX_PLAYERS; i++)
4284     {
4285       if (stored_player[i].active &&
4286           !tape.player_participates[i])
4287       {
4288         struct PlayerInfo *player = &stored_player[i];
4289         int jx = player->jx, jy = player->jy;
4290
4291         player->active = FALSE;
4292         StorePlayer[jx][jy] = 0;
4293         Feld[jx][jy] = EL_EMPTY;
4294       }
4295     }
4296 #endif
4297   }
4298   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4299   {
4300     /* when in single player mode, eliminate all but the first active player */
4301
4302     for (i = 0; i < MAX_PLAYERS; i++)
4303     {
4304       if (stored_player[i].active)
4305       {
4306         for (j = i + 1; j < MAX_PLAYERS; j++)
4307         {
4308           if (stored_player[j].active)
4309           {
4310             struct PlayerInfo *player = &stored_player[j];
4311             int jx = player->jx, jy = player->jy;
4312
4313             player->active = FALSE;
4314             player->present = FALSE;
4315
4316             StorePlayer[jx][jy] = 0;
4317             Feld[jx][jy] = EL_EMPTY;
4318           }
4319         }
4320       }
4321     }
4322   }
4323
4324   /* when recording the game, store which players take part in the game */
4325   if (tape.recording)
4326   {
4327 #if USE_NEW_PLAYER_ASSIGNMENTS
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329       if (stored_player[i].connected)
4330         tape.player_participates[i] = TRUE;
4331 #else
4332     for (i = 0; i < MAX_PLAYERS; i++)
4333       if (stored_player[i].active)
4334         tape.player_participates[i] = TRUE;
4335 #endif
4336   }
4337
4338   if (options.debug)
4339   {
4340     for (i = 0; i < MAX_PLAYERS; i++)
4341     {
4342       struct PlayerInfo *player = &stored_player[i];
4343
4344       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4345              i+1,
4346              player->present,
4347              player->connected,
4348              player->active);
4349       if (local_player == player)
4350         printf("Player  %d is local player.\n", i+1);
4351     }
4352   }
4353
4354   if (BorderElement == EL_EMPTY)
4355   {
4356     SBX_Left = 0;
4357     SBX_Right = lev_fieldx - SCR_FIELDX;
4358     SBY_Upper = 0;
4359     SBY_Lower = lev_fieldy - SCR_FIELDY;
4360   }
4361   else
4362   {
4363     SBX_Left = -1;
4364     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4365     SBY_Upper = -1;
4366     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4367   }
4368
4369 #if NEW_TILESIZE
4370
4371   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4372     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4373
4374   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4375     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4376
4377   if (EVEN(SCR_FIELDX))
4378     SBX_Left--;
4379   if (EVEN(SCR_FIELDY))
4380     SBY_Upper--;
4381
4382 #else
4383
4384   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4385     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4386
4387   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4388     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4389 #endif
4390
4391   /* if local player not found, look for custom element that might create
4392      the player (make some assumptions about the right custom element) */
4393   if (!local_player->present)
4394   {
4395     int start_x = 0, start_y = 0;
4396     int found_rating = 0;
4397     int found_element = EL_UNDEFINED;
4398     int player_nr = local_player->index_nr;
4399
4400     SCAN_PLAYFIELD(x, y)
4401     {
4402       int element = Feld[x][y];
4403       int content;
4404       int xx, yy;
4405       boolean is_player;
4406
4407       if (level.use_start_element[player_nr] &&
4408           level.start_element[player_nr] == element &&
4409           found_rating < 4)
4410       {
4411         start_x = x;
4412         start_y = y;
4413
4414         found_rating = 4;
4415         found_element = element;
4416       }
4417
4418       if (!IS_CUSTOM_ELEMENT(element))
4419         continue;
4420
4421       if (CAN_CHANGE(element))
4422       {
4423         for (i = 0; i < element_info[element].num_change_pages; i++)
4424         {
4425           /* check for player created from custom element as single target */
4426           content = element_info[element].change_page[i].target_element;
4427           is_player = ELEM_IS_PLAYER(content);
4428
4429           if (is_player && (found_rating < 3 ||
4430                             (found_rating == 3 && element < found_element)))
4431           {
4432             start_x = x;
4433             start_y = y;
4434
4435             found_rating = 3;
4436             found_element = element;
4437           }
4438         }
4439       }
4440
4441       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4442       {
4443         /* check for player created from custom element as explosion content */
4444         content = element_info[element].content.e[xx][yy];
4445         is_player = ELEM_IS_PLAYER(content);
4446
4447         if (is_player && (found_rating < 2 ||
4448                           (found_rating == 2 && element < found_element)))
4449         {
4450           start_x = x + xx - 1;
4451           start_y = y + yy - 1;
4452
4453           found_rating = 2;
4454           found_element = element;
4455         }
4456
4457         if (!CAN_CHANGE(element))
4458           continue;
4459
4460         for (i = 0; i < element_info[element].num_change_pages; i++)
4461         {
4462           /* check for player created from custom element as extended target */
4463           content =
4464             element_info[element].change_page[i].target_content.e[xx][yy];
4465
4466           is_player = ELEM_IS_PLAYER(content);
4467
4468           if (is_player && (found_rating < 1 ||
4469                             (found_rating == 1 && element < found_element)))
4470           {
4471             start_x = x + xx - 1;
4472             start_y = y + yy - 1;
4473
4474             found_rating = 1;
4475             found_element = element;
4476           }
4477         }
4478       }
4479     }
4480
4481     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4482                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4483                 start_x - MIDPOSX);
4484
4485     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4486                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4487                 start_y - MIDPOSY);
4488   }
4489   else
4490   {
4491     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4492                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4493                 local_player->jx - MIDPOSX);
4494
4495     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4496                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4497                 local_player->jy - MIDPOSY);
4498   }
4499
4500 #if 0
4501   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4502 #endif
4503
4504 #if 0
4505   /* do not use PLAYING mask for fading out from main screen */
4506   game_status = GAME_MODE_MAIN;
4507 #endif
4508
4509   StopAnimation();
4510
4511   if (!game.restart_level)
4512     CloseDoor(DOOR_CLOSE_1);
4513
4514 #if 1
4515   if (level_editor_test_game)
4516     FadeSkipNextFadeIn();
4517   else
4518     FadeSetEnterScreen();
4519 #else
4520   if (level_editor_test_game)
4521     fading = fading_none;
4522   else
4523     fading = menu.destination;
4524 #endif
4525
4526 #if 1
4527   FadeOut(REDRAW_FIELD);
4528 #else
4529   if (do_fading)
4530     FadeOut(REDRAW_FIELD);
4531 #endif
4532
4533 #if 0
4534   game_status = GAME_MODE_PLAYING;
4535 #endif
4536
4537   /* !!! FIX THIS (START) !!! */
4538   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4539   {
4540     InitGameEngine_EM();
4541
4542     /* blit playfield from scroll buffer to normal back buffer for fading in */
4543     BlitScreenToBitmap_EM(backbuffer);
4544   }
4545   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4546   {
4547     InitGameEngine_SP();
4548
4549     /* blit playfield from scroll buffer to normal back buffer for fading in */
4550     BlitScreenToBitmap_SP(backbuffer);
4551   }
4552   else
4553   {
4554     DrawLevel();
4555     DrawAllPlayers();
4556
4557     /* after drawing the level, correct some elements */
4558     if (game.timegate_time_left == 0)
4559       CloseAllOpenTimegates();
4560
4561 #if NEW_TILESIZE
4562     BlitScreenToBitmap(backbuffer);
4563 #else
4564     /* blit playfield from scroll buffer to normal back buffer for fading in */
4565     if (setup.soft_scrolling)
4566       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4567 #endif
4568
4569     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4570   }
4571   /* !!! FIX THIS (END) !!! */
4572
4573 #if 1
4574   FadeIn(REDRAW_FIELD);
4575 #else
4576   if (do_fading)
4577     FadeIn(REDRAW_FIELD);
4578
4579   BackToFront();
4580 #endif
4581
4582   if (!game.restart_level)
4583   {
4584     /* copy default game door content to main double buffer */
4585 #if 1
4586 #if 1
4587     /* !!! CHECK AGAIN !!! */
4588     SetPanelBackground();
4589     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4590     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4591 #else
4592     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4593
4594     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4595     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4596     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4597                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4598 #endif
4599 #else
4600     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4601                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4602 #endif
4603   }
4604
4605   SetPanelBackground();
4606   SetDrawBackgroundMask(REDRAW_DOOR_1);
4607
4608 #if 1
4609   UpdateAndDisplayGameControlValues();
4610 #else
4611   UpdateGameDoorValues();
4612   DrawGameDoorValues();
4613 #endif
4614
4615   if (!game.restart_level)
4616   {
4617     UnmapGameButtons();
4618     UnmapTapeButtons();
4619     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4620     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4621     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4622     MapGameButtons();
4623     MapTapeButtons();
4624
4625     /* copy actual game door content to door double buffer for OpenDoor() */
4626     BlitBitmap(drawto, bitmap_db_door,
4627                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4628
4629     OpenDoor(DOOR_OPEN_ALL);
4630
4631     PlaySound(SND_GAME_STARTING);
4632
4633     if (setup.sound_music)
4634       PlayLevelMusic();
4635
4636     KeyboardAutoRepeatOffUnlessAutoplay();
4637
4638     if (options.debug)
4639     {
4640       for (i = 0; i < MAX_PLAYERS; i++)
4641         printf("Player %d %sactive.\n",
4642                i + 1, (stored_player[i].active ? "" : "not "));
4643     }
4644   }
4645
4646 #if 1
4647   UnmapAllGadgets();
4648
4649   MapGameButtons();
4650   MapTapeButtons();
4651 #endif
4652
4653   if (!game.restart_level && !tape.playing)
4654   {
4655     LevelStats_incPlayed(level_nr);
4656
4657     SaveLevelSetup_SeriesInfo();
4658
4659 #if 0
4660     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4661 #endif
4662   }
4663
4664   game.restart_level = FALSE;
4665 }
4666
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4668 {
4669   /* this is used for non-R'n'D game engines to update certain engine values */
4670
4671   /* needed to determine if sounds are played within the visible screen area */
4672   scroll_x = actual_scroll_x;
4673   scroll_y = actual_scroll_y;
4674 }
4675
4676 void InitMovDir(int x, int y)
4677 {
4678   int i, element = Feld[x][y];
4679   static int xy[4][2] =
4680   {
4681     {  0, +1 },
4682     { +1,  0 },
4683     {  0, -1 },
4684     { -1,  0 }
4685   };
4686   static int direction[3][4] =
4687   {
4688     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4689     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4690     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4691   };
4692
4693   switch (element)
4694   {
4695     case EL_BUG_RIGHT:
4696     case EL_BUG_UP:
4697     case EL_BUG_LEFT:
4698     case EL_BUG_DOWN:
4699       Feld[x][y] = EL_BUG;
4700       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4701       break;
4702
4703     case EL_SPACESHIP_RIGHT:
4704     case EL_SPACESHIP_UP:
4705     case EL_SPACESHIP_LEFT:
4706     case EL_SPACESHIP_DOWN:
4707       Feld[x][y] = EL_SPACESHIP;
4708       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4709       break;
4710
4711     case EL_BD_BUTTERFLY_RIGHT:
4712     case EL_BD_BUTTERFLY_UP:
4713     case EL_BD_BUTTERFLY_LEFT:
4714     case EL_BD_BUTTERFLY_DOWN:
4715       Feld[x][y] = EL_BD_BUTTERFLY;
4716       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4717       break;
4718
4719     case EL_BD_FIREFLY_RIGHT:
4720     case EL_BD_FIREFLY_UP:
4721     case EL_BD_FIREFLY_LEFT:
4722     case EL_BD_FIREFLY_DOWN:
4723       Feld[x][y] = EL_BD_FIREFLY;
4724       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4725       break;
4726
4727     case EL_PACMAN_RIGHT:
4728     case EL_PACMAN_UP:
4729     case EL_PACMAN_LEFT:
4730     case EL_PACMAN_DOWN:
4731       Feld[x][y] = EL_PACMAN;
4732       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4733       break;
4734
4735     case EL_YAMYAM_LEFT:
4736     case EL_YAMYAM_RIGHT:
4737     case EL_YAMYAM_UP:
4738     case EL_YAMYAM_DOWN:
4739       Feld[x][y] = EL_YAMYAM;
4740       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4741       break;
4742
4743     case EL_SP_SNIKSNAK:
4744       MovDir[x][y] = MV_UP;
4745       break;
4746
4747     case EL_SP_ELECTRON:
4748       MovDir[x][y] = MV_LEFT;
4749       break;
4750
4751     case EL_MOLE_LEFT:
4752     case EL_MOLE_RIGHT:
4753     case EL_MOLE_UP:
4754     case EL_MOLE_DOWN:
4755       Feld[x][y] = EL_MOLE;
4756       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4757       break;
4758
4759     default:
4760       if (IS_CUSTOM_ELEMENT(element))
4761       {
4762         struct ElementInfo *ei = &element_info[element];
4763         int move_direction_initial = ei->move_direction_initial;
4764         int move_pattern = ei->move_pattern;
4765
4766         if (move_direction_initial == MV_START_PREVIOUS)
4767         {
4768           if (MovDir[x][y] != MV_NONE)
4769             return;
4770
4771           move_direction_initial = MV_START_AUTOMATIC;
4772         }
4773
4774         if (move_direction_initial == MV_START_RANDOM)
4775           MovDir[x][y] = 1 << RND(4);
4776         else if (move_direction_initial & MV_ANY_DIRECTION)
4777           MovDir[x][y] = move_direction_initial;
4778         else if (move_pattern == MV_ALL_DIRECTIONS ||
4779                  move_pattern == MV_TURNING_LEFT ||
4780                  move_pattern == MV_TURNING_RIGHT ||
4781                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4782                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4783                  move_pattern == MV_TURNING_RANDOM)
4784           MovDir[x][y] = 1 << RND(4);
4785         else if (move_pattern == MV_HORIZONTAL)
4786           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787         else if (move_pattern == MV_VERTICAL)
4788           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789         else if (move_pattern & MV_ANY_DIRECTION)
4790           MovDir[x][y] = element_info[element].move_pattern;
4791         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792                  move_pattern == MV_ALONG_RIGHT_SIDE)
4793         {
4794           /* use random direction as default start direction */
4795           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796             MovDir[x][y] = 1 << RND(4);
4797
4798           for (i = 0; i < NUM_DIRECTIONS; i++)
4799           {
4800             int x1 = x + xy[i][0];
4801             int y1 = y + xy[i][1];
4802
4803             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4804             {
4805               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806                 MovDir[x][y] = direction[0][i];
4807               else
4808                 MovDir[x][y] = direction[1][i];
4809
4810               break;
4811             }
4812           }
4813         }                
4814       }
4815       else
4816       {
4817         MovDir[x][y] = 1 << RND(4);
4818
4819         if (element != EL_BUG &&
4820             element != EL_SPACESHIP &&
4821             element != EL_BD_BUTTERFLY &&
4822             element != EL_BD_FIREFLY)
4823           break;
4824
4825         for (i = 0; i < NUM_DIRECTIONS; i++)
4826         {
4827           int x1 = x + xy[i][0];
4828           int y1 = y + xy[i][1];
4829
4830           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4831           {
4832             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4833             {
4834               MovDir[x][y] = direction[0][i];
4835               break;
4836             }
4837             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4839             {
4840               MovDir[x][y] = direction[1][i];
4841               break;
4842             }
4843           }
4844         }
4845       }
4846       break;
4847   }
4848
4849   GfxDir[x][y] = MovDir[x][y];
4850 }
4851
4852 void InitAmoebaNr(int x, int y)
4853 {
4854   int i;
4855   int group_nr = AmoebeNachbarNr(x, y);
4856
4857   if (group_nr == 0)
4858   {
4859     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4860     {
4861       if (AmoebaCnt[i] == 0)
4862       {
4863         group_nr = i;
4864         break;
4865       }
4866     }
4867   }
4868
4869   AmoebaNr[x][y] = group_nr;
4870   AmoebaCnt[group_nr]++;
4871   AmoebaCnt2[group_nr]++;
4872 }
4873
4874 static void PlayerWins(struct PlayerInfo *player)
4875 {
4876   player->LevelSolved = TRUE;
4877   player->GameOver = TRUE;
4878
4879   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880                          level.native_em_level->lev->score : player->score);
4881
4882   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4883                                       TimeLeft);
4884   player->LevelSolved_CountingScore = player->score_final;
4885 }
4886
4887 void GameWon()
4888 {
4889   static int time, time_final;
4890   static int score, score_final;
4891   static int game_over_delay_1 = 0;
4892   static int game_over_delay_2 = 0;
4893   int game_over_delay_value_1 = 50;
4894   int game_over_delay_value_2 = 50;
4895
4896   if (!local_player->LevelSolved_GameWon)
4897   {
4898     int i;
4899
4900     /* do not start end game actions before the player stops moving (to exit) */
4901     if (local_player->MovPos)
4902       return;
4903
4904     local_player->LevelSolved_GameWon = TRUE;
4905     local_player->LevelSolved_SaveTape = tape.recording;
4906     local_player->LevelSolved_SaveScore = !tape.playing;
4907
4908     if (!tape.playing)
4909     {
4910       LevelStats_incSolved(level_nr);
4911
4912       SaveLevelSetup_SeriesInfo();
4913
4914 #if 0
4915       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4916 #endif
4917     }
4918
4919     if (tape.auto_play)         /* tape might already be stopped here */
4920       tape.auto_play_level_solved = TRUE;
4921
4922 #if 1
4923     TapeStop();
4924 #endif
4925
4926     game_over_delay_1 = game_over_delay_value_1;
4927     game_over_delay_2 = game_over_delay_value_2;
4928
4929     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930     score = score_final = local_player->score_final;
4931
4932     if (TimeLeft > 0)
4933     {
4934       time_final = 0;
4935       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4936     }
4937     else if (game.no_time_limit && TimePlayed < 999)
4938     {
4939       time_final = 999;
4940       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4941     }
4942
4943     local_player->score_final = score_final;
4944
4945     if (level_editor_test_game)
4946     {
4947       time = time_final;
4948       score = score_final;
4949
4950 #if 1
4951       local_player->LevelSolved_CountingTime = time;
4952       local_player->LevelSolved_CountingScore = score;
4953
4954       game_panel_controls[GAME_PANEL_TIME].value = time;
4955       game_panel_controls[GAME_PANEL_SCORE].value = score;
4956
4957       DisplayGameControlValues();
4958 #else
4959       DrawGameValue_Time(time);
4960       DrawGameValue_Score(score);
4961 #endif
4962     }
4963
4964     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4965     {
4966       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4967       {
4968         /* close exit door after last player */
4969         if ((AllPlayersGone &&
4970              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4975         {
4976           int element = Feld[ExitX][ExitY];
4977
4978 #if 0
4979           if (element == EL_EM_EXIT_OPEN ||
4980               element == EL_EM_STEEL_EXIT_OPEN)
4981           {
4982             Bang(ExitX, ExitY);
4983           }
4984           else
4985 #endif
4986           {
4987             Feld[ExitX][ExitY] =
4988               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4989                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4990                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4991                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4992                EL_EM_STEEL_EXIT_CLOSING);
4993
4994             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4995           }
4996         }
4997
4998         /* player disappears */
4999         DrawLevelField(ExitX, ExitY);
5000       }
5001
5002       for (i = 0; i < MAX_PLAYERS; i++)
5003       {
5004         struct PlayerInfo *player = &stored_player[i];
5005
5006         if (player->present)
5007         {
5008           RemovePlayer(player);
5009
5010           /* player disappears */
5011           DrawLevelField(player->jx, player->jy);
5012         }
5013       }
5014     }
5015
5016     PlaySound(SND_GAME_WINNING);
5017   }
5018
5019   if (game_over_delay_1 > 0)
5020   {
5021     game_over_delay_1--;
5022
5023     return;
5024   }
5025
5026   if (time != time_final)
5027   {
5028     int time_to_go = ABS(time_final - time);
5029     int time_count_dir = (time < time_final ? +1 : -1);
5030     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5031
5032     time  += time_count_steps * time_count_dir;
5033     score += time_count_steps * level.score[SC_TIME_BONUS];
5034
5035 #if 1
5036     local_player->LevelSolved_CountingTime = time;
5037     local_player->LevelSolved_CountingScore = score;
5038
5039     game_panel_controls[GAME_PANEL_TIME].value = time;
5040     game_panel_controls[GAME_PANEL_SCORE].value = score;
5041
5042     DisplayGameControlValues();
5043 #else
5044     DrawGameValue_Time(time);
5045     DrawGameValue_Score(score);
5046 #endif
5047
5048     if (time == time_final)
5049       StopSound(SND_GAME_LEVELTIME_BONUS);
5050     else if (setup.sound_loops)
5051       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5052     else
5053       PlaySound(SND_GAME_LEVELTIME_BONUS);
5054
5055     return;
5056   }
5057
5058   local_player->LevelSolved_PanelOff = TRUE;
5059
5060   if (game_over_delay_2 > 0)
5061   {
5062     game_over_delay_2--;
5063
5064     return;
5065   }
5066
5067 #if 1
5068   GameEnd();
5069 #endif
5070 }
5071
5072 void GameEnd()
5073 {
5074   int hi_pos;
5075   boolean raise_level = FALSE;
5076
5077   local_player->LevelSolved_GameEnd = TRUE;
5078
5079   CloseDoor(DOOR_CLOSE_1);
5080
5081   if (local_player->LevelSolved_SaveTape)
5082   {
5083 #if 0
5084     TapeStop();
5085 #endif
5086
5087 #if 1
5088     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5089 #else
5090     SaveTape(tape.level_nr);            /* ask to save tape */
5091 #endif
5092   }
5093
5094   if (level_editor_test_game)
5095   {
5096     game_status = GAME_MODE_MAIN;
5097
5098 #if 1
5099     DrawAndFadeInMainMenu(REDRAW_FIELD);
5100 #else
5101     DrawMainMenu();
5102 #endif
5103
5104     return;
5105   }
5106
5107   if (!local_player->LevelSolved_SaveScore)
5108   {
5109 #if 1
5110     FadeOut(REDRAW_FIELD);
5111 #endif
5112
5113     game_status = GAME_MODE_MAIN;
5114
5115     DrawAndFadeInMainMenu(REDRAW_FIELD);
5116
5117     return;
5118   }
5119
5120   if (level_nr == leveldir_current->handicap_level)
5121   {
5122     leveldir_current->handicap_level++;
5123
5124     SaveLevelSetup_SeriesInfo();
5125   }
5126
5127   if (level_nr < leveldir_current->last_level)
5128     raise_level = TRUE;                 /* advance to next level */
5129
5130   if ((hi_pos = NewHiScore()) >= 0) 
5131   {
5132     game_status = GAME_MODE_SCORES;
5133
5134     DrawHallOfFame(hi_pos);
5135
5136     if (raise_level)
5137     {
5138       level_nr++;
5139       TapeErase();
5140     }
5141   }
5142   else
5143   {
5144 #if 1
5145     FadeOut(REDRAW_FIELD);
5146 #endif
5147
5148     game_status = GAME_MODE_MAIN;
5149
5150     if (raise_level)
5151     {
5152       level_nr++;
5153       TapeErase();
5154     }
5155
5156     DrawAndFadeInMainMenu(REDRAW_FIELD);
5157   }
5158 }
5159
5160 int NewHiScore()
5161 {
5162   int k, l;
5163   int position = -1;
5164
5165   LoadScore(level_nr);
5166
5167   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5169     return -1;
5170
5171   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5172   {
5173     if (local_player->score_final > highscore[k].Score)
5174     {
5175       /* player has made it to the hall of fame */
5176
5177       if (k < MAX_SCORE_ENTRIES - 1)
5178       {
5179         int m = MAX_SCORE_ENTRIES - 1;
5180
5181 #ifdef ONE_PER_NAME
5182         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183           if (strEqual(setup.player_name, highscore[l].Name))
5184             m = l;
5185         if (m == k)     /* player's new highscore overwrites his old one */
5186           goto put_into_list;
5187 #endif
5188
5189         for (l = m; l > k; l--)
5190         {
5191           strcpy(highscore[l].Name, highscore[l - 1].Name);
5192           highscore[l].Score = highscore[l - 1].Score;
5193         }
5194       }
5195
5196 #ifdef ONE_PER_NAME
5197       put_into_list:
5198 #endif
5199       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201       highscore[k].Score = local_player->score_final; 
5202       position = k;
5203       break;
5204     }
5205
5206 #ifdef ONE_PER_NAME
5207     else if (!strncmp(setup.player_name, highscore[k].Name,
5208                       MAX_PLAYER_NAME_LEN))
5209       break;    /* player already there with a higher score */
5210 #endif
5211
5212   }
5213
5214   if (position >= 0) 
5215     SaveScore(level_nr);
5216
5217   return position;
5218 }
5219
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5221 {
5222   int element = Feld[x][y];
5223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5225   int horiz_move = (dx != 0);
5226   int sign = (horiz_move ? dx : dy);
5227   int step = sign * element_info[element].move_stepsize;
5228
5229   /* special values for move stepsize for spring and things on conveyor belt */
5230   if (horiz_move)
5231   {
5232     if (CAN_FALL(element) &&
5233         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235     else if (element == EL_SPRING)
5236       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5237   }
5238
5239   return step;
5240 }
5241
5242 inline static int getElementMoveStepsize(int x, int y)
5243 {
5244   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5245 }
5246
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5248 {
5249   if (player->GfxAction != action || player->GfxDir != dir)
5250   {
5251 #if 0
5252     printf("Player frame reset! (%d => %d, %d => %d)\n",
5253            player->GfxAction, action, player->GfxDir, dir);
5254 #endif
5255
5256     player->GfxAction = action;
5257     player->GfxDir = dir;
5258     player->Frame = 0;
5259     player->StepFrame = 0;
5260   }
5261 }
5262
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5265 {
5266   int element = Feld[x][y];
5267   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268   int last_gfx_frame = GfxFrame[x][y];
5269
5270   if (graphic_info[graphic].anim_global_sync)
5271     GfxFrame[x][y] = FrameCounter;
5272   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273     GfxFrame[x][y] = CustomValue[x][y];
5274   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275     GfxFrame[x][y] = element_info[element].collect_score;
5276   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277     GfxFrame[x][y] = ChangeDelay[x][y];
5278
5279   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280     DrawLevelGraphicAnimation(x, y, graphic);
5281 }
5282 #endif
5283
5284 static void ResetGfxAnimation(int x, int y)
5285 {
5286   GfxAction[x][y] = ACTION_DEFAULT;
5287   GfxDir[x][y] = MovDir[x][y];
5288   GfxFrame[x][y] = 0;
5289
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291   ResetGfxFrame(x, y, FALSE);
5292 #endif
5293 }
5294
5295 static void ResetRandomAnimationValue(int x, int y)
5296 {
5297   GfxRandom[x][y] = INIT_GFX_RANDOM();
5298 }
5299
5300 void InitMovingField(int x, int y, int direction)
5301 {
5302   int element = Feld[x][y];
5303   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5305   int newx = x + dx;
5306   int newy = y + dy;
5307   boolean is_moving_before, is_moving_after;
5308 #if 0
5309   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5310 #endif
5311
5312   /* check if element was/is moving or being moved before/after mode change */
5313 #if 1
5314 #if 1
5315   is_moving_before = (WasJustMoving[x][y] != 0);
5316 #else
5317   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318   is_moving_before = WasJustMoving[x][y];
5319 #endif
5320 #else
5321   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5322 #endif
5323   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5324
5325   /* reset animation only for moving elements which change direction of moving
5326      or which just started or stopped moving
5327      (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5329 #if 1
5330   if (is_moving_before != is_moving_after ||
5331       direction != MovDir[x][y])
5332     ResetGfxAnimation(x, y);
5333 #else
5334   if ((is_moving_before || is_moving_after) && !continues_moving)
5335     ResetGfxAnimation(x, y);
5336 #endif
5337 #else
5338   if (!continues_moving)
5339     ResetGfxAnimation(x, y);
5340 #endif
5341
5342   MovDir[x][y] = direction;
5343   GfxDir[x][y] = direction;
5344
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347                      direction == MV_DOWN && CAN_FALL(element) ?
5348                      ACTION_FALLING : ACTION_MOVING);
5349 #else
5350   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351                      ACTION_FALLING : ACTION_MOVING);
5352 #endif
5353
5354   /* this is needed for CEs with property "can move" / "not moving" */
5355
5356   if (is_moving_after)
5357   {
5358     if (Feld[newx][newy] == EL_EMPTY)
5359       Feld[newx][newy] = EL_BLOCKED;
5360
5361     MovDir[newx][newy] = MovDir[x][y];
5362
5363 #if USE_NEW_CUSTOM_VALUE
5364     CustomValue[newx][newy] = CustomValue[x][y];
5365 #endif
5366
5367     GfxFrame[newx][newy] = GfxFrame[x][y];
5368     GfxRandom[newx][newy] = GfxRandom[x][y];
5369     GfxAction[newx][newy] = GfxAction[x][y];
5370     GfxDir[newx][newy] = GfxDir[x][y];
5371   }
5372 }
5373
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5375 {
5376   int direction = MovDir[x][y];
5377   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5379
5380   *goes_to_x = newx;
5381   *goes_to_y = newy;
5382 }
5383
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5385 {
5386   int oldx = x, oldy = y;
5387   int direction = MovDir[x][y];
5388
5389   if (direction == MV_LEFT)
5390     oldx++;
5391   else if (direction == MV_RIGHT)
5392     oldx--;
5393   else if (direction == MV_UP)
5394     oldy++;
5395   else if (direction == MV_DOWN)
5396     oldy--;
5397
5398   *comes_from_x = oldx;
5399   *comes_from_y = oldy;
5400 }
5401
5402 int MovingOrBlocked2Element(int x, int y)
5403 {
5404   int element = Feld[x][y];
5405
5406   if (element == EL_BLOCKED)
5407   {
5408     int oldx, oldy;
5409
5410     Blocked2Moving(x, y, &oldx, &oldy);
5411     return Feld[oldx][oldy];
5412   }
5413   else
5414     return element;
5415 }
5416
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5418 {
5419   /* like MovingOrBlocked2Element(), but if element is moving
5420      and (x,y) is the field the moving element is just leaving,
5421      return EL_BLOCKED instead of the element value */
5422   int element = Feld[x][y];
5423
5424   if (IS_MOVING(x, y))
5425   {
5426     if (element == EL_BLOCKED)
5427     {
5428       int oldx, oldy;
5429
5430       Blocked2Moving(x, y, &oldx, &oldy);
5431       return Feld[oldx][oldy];
5432     }
5433     else
5434       return EL_BLOCKED;
5435   }
5436   else
5437     return element;
5438 }
5439
5440 static void RemoveField(int x, int y)
5441 {
5442   Feld[x][y] = EL_EMPTY;
5443
5444   MovPos[x][y] = 0;
5445   MovDir[x][y] = 0;
5446   MovDelay[x][y] = 0;
5447
5448 #if USE_NEW_CUSTOM_VALUE
5449   CustomValue[x][y] = 0;
5450 #endif
5451
5452   AmoebaNr[x][y] = 0;
5453   ChangeDelay[x][y] = 0;
5454   ChangePage[x][y] = -1;
5455   Pushed[x][y] = FALSE;
5456
5457 #if 0
5458   ExplodeField[x][y] = EX_TYPE_NONE;
5459 #endif
5460
5461   GfxElement[x][y] = EL_UNDEFINED;
5462   GfxAction[x][y] = ACTION_DEFAULT;
5463   GfxDir[x][y] = MV_NONE;
5464 #if 0
5465   /* !!! this would prevent the removed tile from being redrawn !!! */
5466   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5467 #endif
5468 }
5469
5470 void RemoveMovingField(int x, int y)
5471 {
5472   int oldx = x, oldy = y, newx = x, newy = y;
5473   int element = Feld[x][y];
5474   int next_element = EL_UNDEFINED;
5475
5476   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5477     return;
5478
5479   if (IS_MOVING(x, y))
5480   {
5481     Moving2Blocked(x, y, &newx, &newy);
5482
5483     if (Feld[newx][newy] != EL_BLOCKED)
5484     {
5485       /* element is moving, but target field is not free (blocked), but
5486          already occupied by something different (example: acid pool);
5487          in this case, only remove the moving field, but not the target */
5488
5489       RemoveField(oldx, oldy);
5490
5491       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5492
5493       TEST_DrawLevelField(oldx, oldy);
5494
5495       return;
5496     }
5497   }
5498   else if (element == EL_BLOCKED)
5499   {
5500     Blocked2Moving(x, y, &oldx, &oldy);
5501     if (!IS_MOVING(oldx, oldy))
5502       return;
5503   }
5504
5505   if (element == EL_BLOCKED &&
5506       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512     next_element = get_next_element(Feld[oldx][oldy]);
5513
5514   RemoveField(oldx, oldy);
5515   RemoveField(newx, newy);
5516
5517   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5518
5519   if (next_element != EL_UNDEFINED)
5520     Feld[oldx][oldy] = next_element;
5521
5522   TEST_DrawLevelField(oldx, oldy);
5523   TEST_DrawLevelField(newx, newy);
5524 }
5525
5526 void DrawDynamite(int x, int y)
5527 {
5528   int sx = SCREENX(x), sy = SCREENY(y);
5529   int graphic = el2img(Feld[x][y]);
5530   int frame;
5531
5532   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5533     return;
5534
5535   if (IS_WALKABLE_INSIDE(Back[x][y]))
5536     return;
5537
5538   if (Back[x][y])
5539     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540   else if (Store[x][y])
5541     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5542
5543   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5544
5545   if (Back[x][y] || Store[x][y])
5546     DrawGraphicThruMask(sx, sy, graphic, frame);
5547   else
5548     DrawGraphic(sx, sy, graphic, frame);
5549 }
5550
5551 void CheckDynamite(int x, int y)
5552 {
5553   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5554   {
5555     MovDelay[x][y]--;
5556
5557     if (MovDelay[x][y] != 0)
5558     {
5559       DrawDynamite(x, y);
5560       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5561
5562       return;
5563     }
5564   }
5565
5566   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5567
5568   Bang(x, y);
5569 }
5570
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5572 {
5573   boolean num_checked_players = 0;
5574   int i;
5575
5576   for (i = 0; i < MAX_PLAYERS; i++)
5577   {
5578     if (stored_player[i].active)
5579     {
5580       int sx = stored_player[i].jx;
5581       int sy = stored_player[i].jy;
5582
5583       if (num_checked_players == 0)
5584       {
5585         *sx1 = *sx2 = sx;
5586         *sy1 = *sy2 = sy;
5587       }
5588       else
5589       {
5590         *sx1 = MIN(*sx1, sx);
5591         *sy1 = MIN(*sy1, sy);
5592         *sx2 = MAX(*sx2, sx);
5593         *sy2 = MAX(*sy2, sy);
5594       }
5595
5596       num_checked_players++;
5597     }
5598   }
5599 }
5600
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5602 {
5603   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5604
5605   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5606
5607   return (sx2 - sx1 < SCR_FIELDX &&
5608           sy2 - sy1 < SCR_FIELDY);
5609 }
5610
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5612 {
5613   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5614
5615   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5616
5617   *sx = (sx1 + sx2) / 2;
5618   *sy = (sy1 + sy2) / 2;
5619 }
5620
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622                         boolean center_screen, boolean quick_relocation)
5623 {
5624   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625   boolean no_delay = (tape.warp_forward);
5626   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5628
5629   if (quick_relocation)
5630   {
5631     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5632     {
5633       if (!level.shifted_relocation || center_screen)
5634       {
5635         /* quick relocation (without scrolling), with centering of screen */
5636
5637         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5638                     x > SBX_Right + MIDPOSX ? SBX_Right :
5639                     x - MIDPOSX);
5640
5641         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643                     y - MIDPOSY);
5644       }
5645       else
5646       {
5647         /* quick relocation (without scrolling), but do not center screen */
5648
5649         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5650                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5651                                old_x - MIDPOSX);
5652
5653         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655                                old_y - MIDPOSY);
5656
5657         int offset_x = x + (scroll_x - center_scroll_x);
5658         int offset_y = y + (scroll_y - center_scroll_y);
5659
5660         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5661                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662                     offset_x - MIDPOSX);
5663
5664         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666                     offset_y - MIDPOSY);
5667       }
5668     }
5669     else
5670     {
5671 #if 1
5672       if (!level.shifted_relocation || center_screen)
5673       {
5674         /* quick relocation (without scrolling), with centering of screen */
5675
5676         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5677                     x > SBX_Right + MIDPOSX ? SBX_Right :
5678                     x - MIDPOSX);
5679
5680         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5682                     y - MIDPOSY);
5683       }
5684       else
5685       {
5686         /* quick relocation (without scrolling), but do not center screen */
5687
5688         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5689                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5690                                old_x - MIDPOSX);
5691
5692         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5694                                old_y - MIDPOSY);
5695
5696         int offset_x = x + (scroll_x - center_scroll_x);
5697         int offset_y = y + (scroll_y - center_scroll_y);
5698
5699         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5700                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701                     offset_x - MIDPOSX);
5702
5703         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705                     offset_y - MIDPOSY);
5706       }
5707 #else
5708       /* quick relocation (without scrolling), inside visible screen area */
5709
5710       int offset = game.scroll_delay_value;
5711
5712       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5713           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5715
5716       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5717           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5719
5720       /* don't scroll over playfield boundaries */
5721       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5723
5724       /* don't scroll over playfield boundaries */
5725       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5727 #endif
5728     }
5729
5730     RedrawPlayfield(TRUE, 0,0,0,0);
5731   }
5732   else
5733   {
5734 #if 1
5735     int scroll_xx, scroll_yy;
5736
5737     if (!level.shifted_relocation || center_screen)
5738     {
5739       /* visible relocation (with scrolling), with centering of screen */
5740
5741       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5742                    x > SBX_Right + MIDPOSX ? SBX_Right :
5743                    x - MIDPOSX);
5744
5745       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5747                    y - MIDPOSY);
5748     }
5749     else
5750     {
5751       /* visible relocation (with scrolling), but do not center screen */
5752
5753       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5754                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5755                              old_x - MIDPOSX);
5756
5757       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5759                              old_y - MIDPOSY);
5760
5761       int offset_x = x + (scroll_x - center_scroll_x);
5762       int offset_y = y + (scroll_y - center_scroll_y);
5763
5764       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5765                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766                    offset_x - MIDPOSX);
5767
5768       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770                    offset_y - MIDPOSY);
5771     }
5772
5773 #else
5774
5775     /* visible relocation (with scrolling), with centering of screen */
5776
5777     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5778                      x > SBX_Right + MIDPOSX ? SBX_Right :
5779                      x - MIDPOSX);
5780
5781     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5783                      y - MIDPOSY);
5784 #endif
5785
5786     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5787
5788     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5789     {
5790       int dx = 0, dy = 0;
5791       int fx = FX, fy = FY;
5792
5793       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5795
5796       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5797         break;
5798
5799       scroll_x -= dx;
5800       scroll_y -= dy;
5801
5802       fx += dx * TILEX / 2;
5803       fy += dy * TILEY / 2;
5804
5805       ScrollLevel(dx, dy);
5806       DrawAllPlayers();
5807
5808       /* scroll in two steps of half tile size to make things smoother */
5809       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5810       FlushDisplay();
5811       Delay(wait_delay_value);
5812
5813       /* scroll second step to align at full tile size */
5814       BackToFront();
5815       Delay(wait_delay_value);
5816     }
5817
5818     DrawAllPlayers();
5819     BackToFront();
5820     Delay(wait_delay_value);
5821   }
5822 }
5823
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5825 {
5826   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827   int player_nr = GET_PLAYER_NR(el_player);
5828   struct PlayerInfo *player = &stored_player[player_nr];
5829   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830   boolean no_delay = (tape.warp_forward);
5831   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833   int old_jx = player->jx;
5834   int old_jy = player->jy;
5835   int old_element = Feld[old_jx][old_jy];
5836   int element = Feld[jx][jy];
5837   boolean player_relocated = (old_jx != jx || old_jy != jy);
5838
5839   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5841   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5843   int leave_side_horiz = move_dir_horiz;
5844   int leave_side_vert  = move_dir_vert;
5845   int enter_side = enter_side_horiz | enter_side_vert;
5846   int leave_side = leave_side_horiz | leave_side_vert;
5847
5848   if (player->GameOver)         /* do not reanimate dead player */
5849     return;
5850
5851   if (!player_relocated)        /* no need to relocate the player */
5852     return;
5853
5854   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5855   {
5856     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5857     DrawLevelField(jx, jy);
5858   }
5859
5860   if (player->present)
5861   {
5862     while (player->MovPos)
5863     {
5864       ScrollPlayer(player, SCROLL_GO_ON);
5865       ScrollScreen(NULL, SCROLL_GO_ON);
5866
5867       AdvanceFrameAndPlayerCounters(player->index_nr);
5868
5869       DrawPlayer(player);
5870
5871       BackToFront();
5872       Delay(wait_delay_value);
5873     }
5874
5875     DrawPlayer(player);         /* needed here only to cleanup last field */
5876     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5877
5878     player->is_moving = FALSE;
5879   }
5880
5881   if (IS_CUSTOM_ELEMENT(old_element))
5882     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5883                                CE_LEFT_BY_PLAYER,
5884                                player->index_bit, leave_side);
5885
5886   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5887                                       CE_PLAYER_LEAVES_X,
5888                                       player->index_bit, leave_side);
5889
5890   Feld[jx][jy] = el_player;
5891   InitPlayerField(jx, jy, el_player, TRUE);
5892
5893   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894      possible that the relocation target field did not contain a player element,
5895      but a walkable element, to which the new player was relocated -- in this
5896      case, restore that (already initialized!) element on the player field */
5897   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5898   {
5899     Feld[jx][jy] = element;     /* restore previously existing element */
5900 #if 0
5901     /* !!! do not initialize already initialized element a second time !!! */
5902     /* (this causes at least problems with "element creation" CE trigger for
5903        already existing elements, and existing Sokoban fields counted twice) */
5904     InitField(jx, jy, FALSE);
5905 #endif
5906   }
5907
5908   /* only visually relocate centered player */
5909   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910                      FALSE, level.instant_relocation);
5911
5912   TestIfPlayerTouchesBadThing(jx, jy);
5913   TestIfPlayerTouchesCustomElement(jx, jy);
5914
5915   if (IS_CUSTOM_ELEMENT(element))
5916     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917                                player->index_bit, enter_side);
5918
5919   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920                                       player->index_bit, enter_side);
5921
5922 #if 1
5923   if (player->is_switching)
5924   {
5925     /* ensure that relocation while still switching an element does not cause
5926        a new element to be treated as also switched directly after relocation
5927        (this is important for teleporter switches that teleport the player to
5928        a place where another teleporter switch is in the same direction, which
5929        would then incorrectly be treated as immediately switched before the
5930        direction key that caused the switch was released) */
5931
5932     player->switch_x += jx - old_jx;
5933     player->switch_y += jy - old_jy;
5934   }
5935 #endif
5936 }
5937
5938 void Explode(int ex, int ey, int phase, int mode)
5939 {
5940   int x, y;
5941   int last_phase;
5942   int border_element;
5943
5944   /* !!! eliminate this variable !!! */
5945   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5946
5947   if (game.explosions_delayed)
5948   {
5949     ExplodeField[ex][ey] = mode;
5950     return;
5951   }
5952
5953   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5954   {
5955     int center_element = Feld[ex][ey];
5956     int artwork_element, explosion_element;     /* set these values later */
5957
5958 #if 0
5959     /* --- This is only really needed (and now handled) in "Impact()". --- */
5960     /* do not explode moving elements that left the explode field in time */
5961     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962         center_element == EL_EMPTY &&
5963         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5964       return;
5965 #endif
5966
5967 #if 0
5968     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969     if (mode == EX_TYPE_NORMAL ||
5970         mode == EX_TYPE_CENTER ||
5971         mode == EX_TYPE_CROSS)
5972       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5973 #endif
5974
5975     /* remove things displayed in background while burning dynamite */
5976     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5977       Back[ex][ey] = 0;
5978
5979     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5980     {
5981       /* put moving element to center field (and let it explode there) */
5982       center_element = MovingOrBlocked2Element(ex, ey);
5983       RemoveMovingField(ex, ey);
5984       Feld[ex][ey] = center_element;
5985     }
5986
5987     /* now "center_element" is finally determined -- set related values now */
5988     artwork_element = center_element;           /* for custom player artwork */
5989     explosion_element = center_element;         /* for custom player artwork */
5990
5991     if (IS_PLAYER(ex, ey))
5992     {
5993       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5994
5995       artwork_element = stored_player[player_nr].artwork_element;
5996
5997       if (level.use_explosion_element[player_nr])
5998       {
5999         explosion_element = level.explosion_element[player_nr];
6000         artwork_element = explosion_element;
6001       }
6002     }
6003
6004 #if 1
6005     if (mode == EX_TYPE_NORMAL ||
6006         mode == EX_TYPE_CENTER ||
6007         mode == EX_TYPE_CROSS)
6008       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6009 #endif
6010
6011     last_phase = element_info[explosion_element].explosion_delay + 1;
6012
6013     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6014     {
6015       int xx = x - ex + 1;
6016       int yy = y - ey + 1;
6017       int element;
6018
6019       if (!IN_LEV_FIELD(x, y) ||
6020           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6022         continue;
6023
6024       element = Feld[x][y];
6025
6026       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6027       {
6028         element = MovingOrBlocked2Element(x, y);
6029
6030         if (!IS_EXPLOSION_PROOF(element))
6031           RemoveMovingField(x, y);
6032       }
6033
6034       /* indestructible elements can only explode in center (but not flames) */
6035       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036                                            mode == EX_TYPE_BORDER)) ||
6037           element == EL_FLAMES)
6038         continue;
6039
6040       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041          behaviour, for example when touching a yamyam that explodes to rocks
6042          with active deadly shield, a rock is created under the player !!! */
6043       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6044 #if 0
6045       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6048 #else
6049       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6050 #endif
6051       {
6052         if (IS_ACTIVE_BOMB(element))
6053         {
6054           /* re-activate things under the bomb like gate or penguin */
6055           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6056           Back[x][y] = 0;
6057         }
6058
6059         continue;
6060       }
6061
6062       /* save walkable background elements while explosion on same tile */
6063       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065         Back[x][y] = element;
6066
6067       /* ignite explodable elements reached by other explosion */
6068       if (element == EL_EXPLOSION)
6069         element = Store2[x][y];
6070
6071       if (AmoebaNr[x][y] &&
6072           (element == EL_AMOEBA_FULL ||
6073            element == EL_BD_AMOEBA ||
6074            element == EL_AMOEBA_GROWING))
6075       {
6076         AmoebaCnt[AmoebaNr[x][y]]--;
6077         AmoebaCnt2[AmoebaNr[x][y]]--;
6078       }
6079
6080       RemoveField(x, y);
6081
6082       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6083       {
6084         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6085
6086         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6087
6088         if (PLAYERINFO(ex, ey)->use_murphy)
6089           Store[x][y] = EL_EMPTY;
6090       }
6091
6092       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094       else if (ELEM_IS_PLAYER(center_element))
6095         Store[x][y] = EL_EMPTY;
6096       else if (center_element == EL_YAMYAM)
6097         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099         Store[x][y] = element_info[center_element].content.e[xx][yy];
6100 #if 1
6101       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103          otherwise) -- FIX THIS !!! */
6104       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105         Store[x][y] = element_info[element].content.e[1][1];
6106 #else
6107       else if (!CAN_EXPLODE(element))
6108         Store[x][y] = element_info[element].content.e[1][1];
6109 #endif
6110       else
6111         Store[x][y] = EL_EMPTY;
6112
6113       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114           center_element == EL_AMOEBA_TO_DIAMOND)
6115         Store2[x][y] = element;
6116
6117       Feld[x][y] = EL_EXPLOSION;
6118       GfxElement[x][y] = artwork_element;
6119
6120       ExplodePhase[x][y] = 1;
6121       ExplodeDelay[x][y] = last_phase;
6122
6123       Stop[x][y] = TRUE;
6124     }
6125
6126     if (center_element == EL_YAMYAM)
6127       game.yamyam_content_nr =
6128         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6129
6130     return;
6131   }
6132
6133   if (Stop[ex][ey])
6134     return;
6135
6136   x = ex;
6137   y = ey;
6138
6139   if (phase == 1)
6140     GfxFrame[x][y] = 0;         /* restart explosion animation */
6141
6142   last_phase = ExplodeDelay[x][y];
6143
6144   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6145
6146 #ifdef DEBUG
6147
6148   /* activate this even in non-DEBUG version until cause for crash in
6149      getGraphicAnimationFrame() (see below) is found and eliminated */
6150
6151 #endif
6152 #if 1
6153
6154 #if 1
6155   /* this can happen if the player leaves an explosion just in time */
6156   if (GfxElement[x][y] == EL_UNDEFINED)
6157     GfxElement[x][y] = EL_EMPTY;
6158 #else
6159   if (GfxElement[x][y] == EL_UNDEFINED)
6160   {
6161     printf("\n\n");
6162     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163     printf("Explode(): This should never happen!\n");
6164     printf("\n\n");
6165
6166     GfxElement[x][y] = EL_EMPTY;
6167   }
6168 #endif
6169
6170 #endif
6171
6172   border_element = Store2[x][y];
6173   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174     border_element = StorePlayer[x][y];
6175
6176   if (phase == element_info[border_element].ignition_delay ||
6177       phase == last_phase)
6178   {
6179     boolean border_explosion = FALSE;
6180
6181     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182         !PLAYER_EXPLOSION_PROTECTED(x, y))
6183     {
6184       KillPlayerUnlessExplosionProtected(x, y);
6185       border_explosion = TRUE;
6186     }
6187     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6188     {
6189       Feld[x][y] = Store2[x][y];
6190       Store2[x][y] = 0;
6191       Bang(x, y);
6192       border_explosion = TRUE;
6193     }
6194     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6195     {
6196       AmoebeUmwandeln(x, y);
6197       Store2[x][y] = 0;
6198       border_explosion = TRUE;
6199     }
6200
6201     /* if an element just explodes due to another explosion (chain-reaction),
6202        do not immediately end the new explosion when it was the last frame of
6203        the explosion (as it would be done in the following "if"-statement!) */
6204     if (border_explosion && phase == last_phase)
6205       return;
6206   }
6207
6208   if (phase == last_phase)
6209   {
6210     int element;
6211
6212     element = Feld[x][y] = Store[x][y];
6213     Store[x][y] = Store2[x][y] = 0;
6214     GfxElement[x][y] = EL_UNDEFINED;
6215
6216     /* player can escape from explosions and might therefore be still alive */
6217     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218         element <= EL_PLAYER_IS_EXPLODING_4)
6219     {
6220       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221       int explosion_element = EL_PLAYER_1 + player_nr;
6222       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6224
6225       if (level.use_explosion_element[player_nr])
6226         explosion_element = level.explosion_element[player_nr];
6227
6228       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229                     element_info[explosion_element].content.e[xx][yy]);
6230     }
6231
6232     /* restore probably existing indestructible background element */
6233     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234       element = Feld[x][y] = Back[x][y];
6235     Back[x][y] = 0;
6236
6237     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238     GfxDir[x][y] = MV_NONE;
6239     ChangeDelay[x][y] = 0;
6240     ChangePage[x][y] = -1;
6241
6242 #if USE_NEW_CUSTOM_VALUE
6243     CustomValue[x][y] = 0;
6244 #endif
6245
6246     InitField_WithBug2(x, y, FALSE);
6247
6248     TEST_DrawLevelField(x, y);
6249
6250     TestIfElementTouchesCustomElement(x, y);
6251
6252     if (GFX_CRUMBLED(element))
6253       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6254
6255     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256       StorePlayer[x][y] = 0;
6257
6258     if (ELEM_IS_PLAYER(element))
6259       RelocatePlayer(x, y, element);
6260   }
6261   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6262   {
6263     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6265
6266     if (phase == delay)
6267       TEST_DrawLevelFieldCrumbled(x, y);
6268
6269     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6270     {
6271       DrawLevelElement(x, y, Back[x][y]);
6272       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6273     }
6274     else if (IS_WALKABLE_UNDER(Back[x][y]))
6275     {
6276       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277       DrawLevelElementThruMask(x, y, Back[x][y]);
6278     }
6279     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6281   }
6282 }
6283
6284 void DynaExplode(int ex, int ey)
6285 {
6286   int i, j;
6287   int dynabomb_element = Feld[ex][ey];
6288   int dynabomb_size = 1;
6289   boolean dynabomb_xl = FALSE;
6290   struct PlayerInfo *player;
6291   static int xy[4][2] =
6292   {
6293     { 0, -1 },
6294     { -1, 0 },
6295     { +1, 0 },
6296     { 0, +1 }
6297   };
6298
6299   if (IS_ACTIVE_BOMB(dynabomb_element))
6300   {
6301     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302     dynabomb_size = player->dynabomb_size;
6303     dynabomb_xl = player->dynabomb_xl;
6304     player->dynabombs_left++;
6305   }
6306
6307   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6308
6309   for (i = 0; i < NUM_DIRECTIONS; i++)
6310   {
6311     for (j = 1; j <= dynabomb_size; j++)
6312     {
6313       int x = ex + j * xy[i][0];
6314       int y = ey + j * xy[i][1];
6315       int element;
6316
6317       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6318         break;
6319
6320       element = Feld[x][y];
6321
6322       /* do not restart explosions of fields with active bombs */
6323       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6324         continue;
6325
6326       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6327
6328       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329           !IS_DIGGABLE(element) && !dynabomb_xl)
6330         break;
6331     }
6332   }
6333 }
6334
6335 void Bang(int x, int y)
6336 {
6337   int element = MovingOrBlocked2Element(x, y);
6338   int explosion_type = EX_TYPE_NORMAL;
6339
6340   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6341   {
6342     struct PlayerInfo *player = PLAYERINFO(x, y);
6343
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345     element = Feld[x][y] = player->initial_element;
6346 #else
6347     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348                             player->element_nr);
6349 #endif
6350
6351     if (level.use_explosion_element[player->index_nr])
6352     {
6353       int explosion_element = level.explosion_element[player->index_nr];
6354
6355       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356         explosion_type = EX_TYPE_CROSS;
6357       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358         explosion_type = EX_TYPE_CENTER;
6359     }
6360   }
6361
6362   switch (element)
6363   {
6364     case EL_BUG:
6365     case EL_SPACESHIP:
6366     case EL_BD_BUTTERFLY:
6367     case EL_BD_FIREFLY:
6368     case EL_YAMYAM:
6369     case EL_DARK_YAMYAM:
6370     case EL_ROBOT:
6371     case EL_PACMAN:
6372     case EL_MOLE:
6373       RaiseScoreElement(element);
6374       break;
6375
6376     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380     case EL_DYNABOMB_INCREASE_NUMBER:
6381     case EL_DYNABOMB_INCREASE_SIZE:
6382     case EL_DYNABOMB_INCREASE_POWER:
6383       explosion_type = EX_TYPE_DYNA;
6384       break;
6385
6386     case EL_DC_LANDMINE:
6387 #if 0
6388     case EL_EM_EXIT_OPEN:
6389     case EL_EM_STEEL_EXIT_OPEN:
6390 #endif
6391       explosion_type = EX_TYPE_CENTER;
6392       break;
6393
6394     case EL_PENGUIN:
6395     case EL_LAMP:
6396     case EL_LAMP_ACTIVE:
6397     case EL_AMOEBA_TO_DIAMOND:
6398       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6399         explosion_type = EX_TYPE_CENTER;
6400       break;
6401
6402     default:
6403       if (element_info[element].explosion_type == EXPLODES_CROSS)
6404         explosion_type = EX_TYPE_CROSS;
6405       else if (element_info[element].explosion_type == EXPLODES_1X1)
6406         explosion_type = EX_TYPE_CENTER;
6407       break;
6408   }
6409
6410   if (explosion_type == EX_TYPE_DYNA)
6411     DynaExplode(x, y);
6412   else
6413     Explode(x, y, EX_PHASE_START, explosion_type);
6414
6415   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6416 }
6417
6418 void SplashAcid(int x, int y)
6419 {
6420   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421       (!IN_LEV_FIELD(x - 1, y - 2) ||
6422        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6424
6425   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426       (!IN_LEV_FIELD(x + 1, y - 2) ||
6427        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6429
6430   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6431 }
6432
6433 static void InitBeltMovement()
6434 {
6435   static int belt_base_element[4] =
6436   {
6437     EL_CONVEYOR_BELT_1_LEFT,
6438     EL_CONVEYOR_BELT_2_LEFT,
6439     EL_CONVEYOR_BELT_3_LEFT,
6440     EL_CONVEYOR_BELT_4_LEFT
6441   };
6442   static int belt_base_active_element[4] =
6443   {
6444     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6448   };
6449
6450   int x, y, i, j;
6451
6452   /* set frame order for belt animation graphic according to belt direction */
6453   for (i = 0; i < NUM_BELTS; i++)
6454   {
6455     int belt_nr = i;
6456
6457     for (j = 0; j < NUM_BELT_PARTS; j++)
6458     {
6459       int element = belt_base_active_element[belt_nr] + j;
6460       int graphic_1 = el2img(element);
6461       int graphic_2 = el2panelimg(element);
6462
6463       if (game.belt_dir[i] == MV_LEFT)
6464       {
6465         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6467       }
6468       else
6469       {
6470         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6471         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6472       }
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(x, y)
6477   {
6478     int element = Feld[x][y];
6479
6480     for (i = 0; i < NUM_BELTS; i++)
6481     {
6482       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6483       {
6484         int e_belt_nr = getBeltNrFromBeltElement(element);
6485         int belt_nr = i;
6486
6487         if (e_belt_nr == belt_nr)
6488         {
6489           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6490
6491           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6492         }
6493       }
6494     }
6495   }
6496 }
6497
6498 static void ToggleBeltSwitch(int x, int y)
6499 {
6500   static int belt_base_element[4] =
6501   {
6502     EL_CONVEYOR_BELT_1_LEFT,
6503     EL_CONVEYOR_BELT_2_LEFT,
6504     EL_CONVEYOR_BELT_3_LEFT,
6505     EL_CONVEYOR_BELT_4_LEFT
6506   };
6507   static int belt_base_active_element[4] =
6508   {
6509     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6513   };
6514   static int belt_base_switch_element[4] =
6515   {
6516     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6520   };
6521   static int belt_move_dir[4] =
6522   {
6523     MV_LEFT,
6524     MV_NONE,
6525     MV_RIGHT,
6526     MV_NONE,
6527   };
6528
6529   int element = Feld[x][y];
6530   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532   int belt_dir = belt_move_dir[belt_dir_nr];
6533   int xx, yy, i;
6534
6535   if (!IS_BELT_SWITCH(element))
6536     return;
6537
6538   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539   game.belt_dir[belt_nr] = belt_dir;
6540
6541   if (belt_dir_nr == 3)
6542     belt_dir_nr = 1;
6543
6544   /* set frame order for belt animation graphic according to belt direction */
6545   for (i = 0; i < NUM_BELT_PARTS; i++)
6546   {
6547     int element = belt_base_active_element[belt_nr] + i;
6548     int graphic_1 = el2img(element);
6549     int graphic_2 = el2panelimg(element);
6550
6551     if (belt_dir == MV_LEFT)
6552     {
6553       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6555     }
6556     else
6557     {
6558       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6559       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6560     }
6561   }
6562
6563   SCAN_PLAYFIELD(xx, yy)
6564   {
6565     int element = Feld[xx][yy];
6566
6567     if (IS_BELT_SWITCH(element))
6568     {
6569       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6570
6571       if (e_belt_nr == belt_nr)
6572       {
6573         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574         TEST_DrawLevelField(xx, yy);
6575       }
6576     }
6577     else if (IS_BELT(element) && belt_dir != MV_NONE)
6578     {
6579       int e_belt_nr = getBeltNrFromBeltElement(element);
6580
6581       if (e_belt_nr == belt_nr)
6582       {
6583         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6584
6585         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586         TEST_DrawLevelField(xx, yy);
6587       }
6588     }
6589     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6590     {
6591       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6592
6593       if (e_belt_nr == belt_nr)
6594       {
6595         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6596
6597         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598         TEST_DrawLevelField(xx, yy);
6599       }
6600     }
6601   }
6602 }
6603
6604 static void ToggleSwitchgateSwitch(int x, int y)
6605 {
6606   int xx, yy;
6607
6608   game.switchgate_pos = !game.switchgate_pos;
6609
6610   SCAN_PLAYFIELD(xx, yy)
6611   {
6612     int element = Feld[xx][yy];
6613
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615     if (element == EL_SWITCHGATE_SWITCH_UP ||
6616         element == EL_SWITCHGATE_SWITCH_DOWN)
6617     {
6618       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619       TEST_DrawLevelField(xx, yy);
6620     }
6621     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6623     {
6624       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625       TEST_DrawLevelField(xx, yy);
6626     }
6627 #else
6628     if (element == EL_SWITCHGATE_SWITCH_UP)
6629     {
6630       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6634     {
6635       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636       TEST_DrawLevelField(xx, yy);
6637     }
6638     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6639     {
6640       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641       TEST_DrawLevelField(xx, yy);
6642     }
6643     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6644     {
6645       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646       TEST_DrawLevelField(xx, yy);
6647     }
6648 #endif
6649     else if (element == EL_SWITCHGATE_OPEN ||
6650              element == EL_SWITCHGATE_OPENING)
6651     {
6652       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6653
6654       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6655     }
6656     else if (element == EL_SWITCHGATE_CLOSED ||
6657              element == EL_SWITCHGATE_CLOSING)
6658     {
6659       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6660
6661       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6662     }
6663   }
6664 }
6665
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6667 {
6668   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6670           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6671           element);
6672 }
6673
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6675 {
6676   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6678           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6679           element);
6680 }
6681
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6683 {
6684   int x, y;
6685
6686   SCAN_PLAYFIELD(x, y)
6687   {
6688     int element = Feld[x][y];
6689
6690     if (element == EL_LIGHT_SWITCH &&
6691         game.light_time_left > 0)
6692     {
6693       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697              game.light_time_left == 0)
6698     {
6699       Feld[x][y] = EL_LIGHT_SWITCH;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_EMC_DRIPPER &&
6703              game.light_time_left > 0)
6704     {
6705       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709              game.light_time_left == 0)
6710     {
6711       Feld[x][y] = EL_EMC_DRIPPER;
6712       TEST_DrawLevelField(x, y);
6713     }
6714     else if (element == EL_INVISIBLE_STEELWALL ||
6715              element == EL_INVISIBLE_WALL ||
6716              element == EL_INVISIBLE_SAND)
6717     {
6718       if (game.light_time_left > 0)
6719         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6720
6721       TEST_DrawLevelField(x, y);
6722
6723       /* uncrumble neighbour fields, if needed */
6724       if (element == EL_INVISIBLE_SAND)
6725         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6726     }
6727     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728              element == EL_INVISIBLE_WALL_ACTIVE ||
6729              element == EL_INVISIBLE_SAND_ACTIVE)
6730     {
6731       if (game.light_time_left == 0)
6732         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6733
6734       TEST_DrawLevelField(x, y);
6735
6736       /* re-crumble neighbour fields, if needed */
6737       if (element == EL_INVISIBLE_SAND)
6738         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6739     }
6740   }
6741 }
6742
6743 static void RedrawAllInvisibleElementsForLenses()
6744 {
6745   int x, y;
6746
6747   SCAN_PLAYFIELD(x, y)
6748   {
6749     int element = Feld[x][y];
6750
6751     if (element == EL_EMC_DRIPPER &&
6752         game.lenses_time_left > 0)
6753     {
6754       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755       TEST_DrawLevelField(x, y);
6756     }
6757     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758              game.lenses_time_left == 0)
6759     {
6760       Feld[x][y] = EL_EMC_DRIPPER;
6761       TEST_DrawLevelField(x, y);
6762     }
6763     else if (element == EL_INVISIBLE_STEELWALL ||
6764              element == EL_INVISIBLE_WALL ||
6765              element == EL_INVISIBLE_SAND)
6766     {
6767       if (game.lenses_time_left > 0)
6768         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6769
6770       TEST_DrawLevelField(x, y);
6771
6772       /* uncrumble neighbour fields, if needed */
6773       if (element == EL_INVISIBLE_SAND)
6774         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6775     }
6776     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777              element == EL_INVISIBLE_WALL_ACTIVE ||
6778              element == EL_INVISIBLE_SAND_ACTIVE)
6779     {
6780       if (game.lenses_time_left == 0)
6781         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6782
6783       TEST_DrawLevelField(x, y);
6784
6785       /* re-crumble neighbour fields, if needed */
6786       if (element == EL_INVISIBLE_SAND)
6787         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6788     }
6789   }
6790 }
6791
6792 static void RedrawAllInvisibleElementsForMagnifier()
6793 {
6794   int x, y;
6795
6796   SCAN_PLAYFIELD(x, y)
6797   {
6798     int element = Feld[x][y];
6799
6800     if (element == EL_EMC_FAKE_GRASS &&
6801         game.magnify_time_left > 0)
6802     {
6803       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804       TEST_DrawLevelField(x, y);
6805     }
6806     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807              game.magnify_time_left == 0)
6808     {
6809       Feld[x][y] = EL_EMC_FAKE_GRASS;
6810       TEST_DrawLevelField(x, y);
6811     }
6812     else if (IS_GATE_GRAY(element) &&
6813              game.magnify_time_left > 0)
6814     {
6815       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817                     IS_EM_GATE_GRAY(element) ?
6818                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819                     IS_EMC_GATE_GRAY(element) ?
6820                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821                     IS_DC_GATE_GRAY(element) ?
6822                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6823                     element);
6824       TEST_DrawLevelField(x, y);
6825     }
6826     else if (IS_GATE_GRAY_ACTIVE(element) &&
6827              game.magnify_time_left == 0)
6828     {
6829       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6832                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6836                     EL_DC_GATE_WHITE_GRAY :
6837                     element);
6838       TEST_DrawLevelField(x, y);
6839     }
6840   }
6841 }
6842
6843 static void ToggleLightSwitch(int x, int y)
6844 {
6845   int element = Feld[x][y];
6846
6847   game.light_time_left =
6848     (element == EL_LIGHT_SWITCH ?
6849      level.time_light * FRAMES_PER_SECOND : 0);
6850
6851   RedrawAllLightSwitchesAndInvisibleElements();
6852 }
6853
6854 static void ActivateTimegateSwitch(int x, int y)
6855 {
6856   int xx, yy;
6857
6858   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6859
6860   SCAN_PLAYFIELD(xx, yy)
6861   {
6862     int element = Feld[xx][yy];
6863
6864     if (element == EL_TIMEGATE_CLOSED ||
6865         element == EL_TIMEGATE_CLOSING)
6866     {
6867       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6869     }
6870
6871     /*
6872     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6873     {
6874       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875       TEST_DrawLevelField(xx, yy);
6876     }
6877     */
6878
6879   }
6880
6881 #if 1
6882   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6884 #else
6885   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6886 #endif
6887 }
6888
6889 void Impact(int x, int y)
6890 {
6891   boolean last_line = (y == lev_fieldy - 1);
6892   boolean object_hit = FALSE;
6893   boolean impact = (last_line || object_hit);
6894   int element = Feld[x][y];
6895   int smashed = EL_STEELWALL;
6896
6897   if (!last_line)       /* check if element below was hit */
6898   {
6899     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6900       return;
6901
6902     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903                                          MovDir[x][y + 1] != MV_DOWN ||
6904                                          MovPos[x][y + 1] <= TILEY / 2));
6905
6906     /* do not smash moving elements that left the smashed field in time */
6907     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6909       object_hit = FALSE;
6910
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6913     {
6914       RemoveMovingField(x, y + 1);
6915       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916       Feld[x][y + 2] = EL_ROCK;
6917       TEST_DrawLevelField(x, y + 2);
6918
6919       object_hit = TRUE;
6920     }
6921
6922     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6923     {
6924       RemoveMovingField(x, y + 1);
6925       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926       Feld[x][y + 2] = EL_ROCK;
6927       TEST_DrawLevelField(x, y + 2);
6928
6929       object_hit = TRUE;
6930     }
6931 #endif
6932
6933     if (object_hit)
6934       smashed = MovingOrBlocked2Element(x, y + 1);
6935
6936     impact = (last_line || object_hit);
6937   }
6938
6939   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6940   {
6941     SplashAcid(x, y + 1);
6942     return;
6943   }
6944
6945   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946   /* only reset graphic animation if graphic really changes after impact */
6947   if (impact &&
6948       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6949   {
6950     ResetGfxAnimation(x, y);
6951     TEST_DrawLevelField(x, y);
6952   }
6953
6954   if (impact && CAN_EXPLODE_IMPACT(element))
6955   {
6956     Bang(x, y);
6957     return;
6958   }
6959   else if (impact && element == EL_PEARL &&
6960            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6961   {
6962     ResetGfxAnimation(x, y);
6963
6964     Feld[x][y] = EL_PEARL_BREAKING;
6965     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6966     return;
6967   }
6968   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6969   {
6970     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6971
6972     return;
6973   }
6974
6975   if (impact && element == EL_AMOEBA_DROP)
6976   {
6977     if (object_hit && IS_PLAYER(x, y + 1))
6978       KillPlayerUnlessEnemyProtected(x, y + 1);
6979     else if (object_hit && smashed == EL_PENGUIN)
6980       Bang(x, y + 1);
6981     else
6982     {
6983       Feld[x][y] = EL_AMOEBA_GROWING;
6984       Store[x][y] = EL_AMOEBA_WET;
6985
6986       ResetRandomAnimationValue(x, y);
6987     }
6988     return;
6989   }
6990
6991   if (object_hit)               /* check which object was hit */
6992   {
6993     if ((CAN_PASS_MAGIC_WALL(element) && 
6994          (smashed == EL_MAGIC_WALL ||
6995           smashed == EL_BD_MAGIC_WALL)) ||
6996         (CAN_PASS_DC_MAGIC_WALL(element) &&
6997          smashed == EL_DC_MAGIC_WALL))
6998     {
6999       int xx, yy;
7000       int activated_magic_wall =
7001         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003          EL_DC_MAGIC_WALL_ACTIVE);
7004
7005       /* activate magic wall / mill */
7006       SCAN_PLAYFIELD(xx, yy)
7007       {
7008         if (Feld[xx][yy] == smashed)
7009           Feld[xx][yy] = activated_magic_wall;
7010       }
7011
7012       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013       game.magic_wall_active = TRUE;
7014
7015       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016                             SND_MAGIC_WALL_ACTIVATING :
7017                             smashed == EL_BD_MAGIC_WALL ?
7018                             SND_BD_MAGIC_WALL_ACTIVATING :
7019                             SND_DC_MAGIC_WALL_ACTIVATING));
7020     }
7021
7022     if (IS_PLAYER(x, y + 1))
7023     {
7024       if (CAN_SMASH_PLAYER(element))
7025       {
7026         KillPlayerUnlessEnemyProtected(x, y + 1);
7027         return;
7028       }
7029     }
7030     else if (smashed == EL_PENGUIN)
7031     {
7032       if (CAN_SMASH_PLAYER(element))
7033       {
7034         Bang(x, y + 1);
7035         return;
7036       }
7037     }
7038     else if (element == EL_BD_DIAMOND)
7039     {
7040       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7041       {
7042         Bang(x, y + 1);
7043         return;
7044       }
7045     }
7046     else if (((element == EL_SP_INFOTRON ||
7047                element == EL_SP_ZONK) &&
7048               (smashed == EL_SP_SNIKSNAK ||
7049                smashed == EL_SP_ELECTRON ||
7050                smashed == EL_SP_DISK_ORANGE)) ||
7051              (element == EL_SP_INFOTRON &&
7052               smashed == EL_SP_DISK_YELLOW))
7053     {
7054       Bang(x, y + 1);
7055       return;
7056     }
7057     else if (CAN_SMASH_EVERYTHING(element))
7058     {
7059       if (IS_CLASSIC_ENEMY(smashed) ||
7060           CAN_EXPLODE_SMASHED(smashed))
7061       {
7062         Bang(x, y + 1);
7063         return;
7064       }
7065       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7066       {
7067         if (smashed == EL_LAMP ||
7068             smashed == EL_LAMP_ACTIVE)
7069         {
7070           Bang(x, y + 1);
7071           return;
7072         }
7073         else if (smashed == EL_NUT)
7074         {
7075           Feld[x][y + 1] = EL_NUT_BREAKING;
7076           PlayLevelSound(x, y, SND_NUT_BREAKING);
7077           RaiseScoreElement(EL_NUT);
7078           return;
7079         }
7080         else if (smashed == EL_PEARL)
7081         {
7082           ResetGfxAnimation(x, y);
7083
7084           Feld[x][y + 1] = EL_PEARL_BREAKING;
7085           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7086           return;
7087         }
7088         else if (smashed == EL_DIAMOND)
7089         {
7090           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7092           return;
7093         }
7094         else if (IS_BELT_SWITCH(smashed))
7095         {
7096           ToggleBeltSwitch(x, y + 1);
7097         }
7098         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7102         {
7103           ToggleSwitchgateSwitch(x, y + 1);
7104         }
7105         else if (smashed == EL_LIGHT_SWITCH ||
7106                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7107         {
7108           ToggleLightSwitch(x, y + 1);
7109         }
7110         else
7111         {
7112 #if 0
7113           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7114 #endif
7115
7116           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7117
7118           CheckElementChangeBySide(x, y + 1, smashed, element,
7119                                    CE_SWITCHED, CH_SIDE_TOP);
7120           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7121                                             CH_SIDE_TOP);
7122         }
7123       }
7124       else
7125       {
7126         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7127       }
7128     }
7129   }
7130
7131   /* play sound of magic wall / mill */
7132   if (!last_line &&
7133       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7136   {
7137     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7143
7144     return;
7145   }
7146
7147   /* play sound of object that hits the ground */
7148   if (last_line || object_hit)
7149     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7150 }
7151
7152 inline static void TurnRoundExt(int x, int y)
7153 {
7154   static struct
7155   {
7156     int dx, dy;
7157   } move_xy[] =
7158   {
7159     {  0,  0 },
7160     { -1,  0 },
7161     { +1,  0 },
7162     {  0,  0 },
7163     {  0, -1 },
7164     {  0,  0 }, { 0, 0 }, { 0, 0 },
7165     {  0, +1 }
7166   };
7167   static struct
7168   {
7169     int left, right, back;
7170   } turn[] =
7171   {
7172     { 0,        0,              0        },
7173     { MV_DOWN,  MV_UP,          MV_RIGHT },
7174     { MV_UP,    MV_DOWN,        MV_LEFT  },
7175     { 0,        0,              0        },
7176     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7177     { 0,        0,              0        },
7178     { 0,        0,              0        },
7179     { 0,        0,              0        },
7180     { MV_RIGHT, MV_LEFT,        MV_UP    }
7181   };
7182
7183   int element = Feld[x][y];
7184   int move_pattern = element_info[element].move_pattern;
7185
7186   int old_move_dir = MovDir[x][y];
7187   int left_dir  = turn[old_move_dir].left;
7188   int right_dir = turn[old_move_dir].right;
7189   int back_dir  = turn[old_move_dir].back;
7190
7191   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7192   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7193   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7194   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7195
7196   int left_x  = x + left_dx,  left_y  = y + left_dy;
7197   int right_x = x + right_dx, right_y = y + right_dy;
7198   int move_x  = x + move_dx,  move_y  = y + move_dy;
7199
7200   int xx, yy;
7201
7202   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7203   {
7204     TestIfBadThingTouchesOtherBadThing(x, y);
7205
7206     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207       MovDir[x][y] = right_dir;
7208     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209       MovDir[x][y] = left_dir;
7210
7211     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7212       MovDelay[x][y] = 9;
7213     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7214       MovDelay[x][y] = 1;
7215   }
7216   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7217   {
7218     TestIfBadThingTouchesOtherBadThing(x, y);
7219
7220     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221       MovDir[x][y] = left_dir;
7222     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223       MovDir[x][y] = right_dir;
7224
7225     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7226       MovDelay[x][y] = 9;
7227     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7228       MovDelay[x][y] = 1;
7229   }
7230   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7231   {
7232     TestIfBadThingTouchesOtherBadThing(x, y);
7233
7234     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235       MovDir[x][y] = left_dir;
7236     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237       MovDir[x][y] = right_dir;
7238
7239     if (MovDir[x][y] != old_move_dir)
7240       MovDelay[x][y] = 9;
7241   }
7242   else if (element == EL_YAMYAM)
7243   {
7244     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7246
7247     if (can_turn_left && can_turn_right)
7248       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249     else if (can_turn_left)
7250       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251     else if (can_turn_right)
7252       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7253     else
7254       MovDir[x][y] = back_dir;
7255
7256     MovDelay[x][y] = 16 + 16 * RND(3);
7257   }
7258   else if (element == EL_DARK_YAMYAM)
7259   {
7260     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7261                                                          left_x, left_y);
7262     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7263                                                          right_x, right_y);
7264
7265     if (can_turn_left && can_turn_right)
7266       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267     else if (can_turn_left)
7268       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269     else if (can_turn_right)
7270       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7271     else
7272       MovDir[x][y] = back_dir;
7273
7274     MovDelay[x][y] = 16 + 16 * RND(3);
7275   }
7276   else if (element == EL_PACMAN)
7277   {
7278     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7280
7281     if (can_turn_left && can_turn_right)
7282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283     else if (can_turn_left)
7284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285     else if (can_turn_right)
7286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7287     else
7288       MovDir[x][y] = back_dir;
7289
7290     MovDelay[x][y] = 6 + RND(40);
7291   }
7292   else if (element == EL_PIG)
7293   {
7294     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297     boolean should_turn_left, should_turn_right, should_move_on;
7298     int rnd_value = 24;
7299     int rnd = RND(rnd_value);
7300
7301     should_turn_left = (can_turn_left &&
7302                         (!can_move_on ||
7303                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304                                                    y + back_dy + left_dy)));
7305     should_turn_right = (can_turn_right &&
7306                          (!can_move_on ||
7307                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308                                                     y + back_dy + right_dy)));
7309     should_move_on = (can_move_on &&
7310                       (!can_turn_left ||
7311                        !can_turn_right ||
7312                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313                                                  y + move_dy + left_dy) ||
7314                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315                                                  y + move_dy + right_dy)));
7316
7317     if (should_turn_left || should_turn_right || should_move_on)
7318     {
7319       if (should_turn_left && should_turn_right && should_move_on)
7320         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7321                         rnd < 2 * rnd_value / 3 ? right_dir :
7322                         old_move_dir);
7323       else if (should_turn_left && should_turn_right)
7324         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325       else if (should_turn_left && should_move_on)
7326         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327       else if (should_turn_right && should_move_on)
7328         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329       else if (should_turn_left)
7330         MovDir[x][y] = left_dir;
7331       else if (should_turn_right)
7332         MovDir[x][y] = right_dir;
7333       else if (should_move_on)
7334         MovDir[x][y] = old_move_dir;
7335     }
7336     else if (can_move_on && rnd > rnd_value / 8)
7337       MovDir[x][y] = old_move_dir;
7338     else if (can_turn_left && can_turn_right)
7339       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340     else if (can_turn_left && rnd > rnd_value / 8)
7341       MovDir[x][y] = left_dir;
7342     else if (can_turn_right && rnd > rnd_value/8)
7343       MovDir[x][y] = right_dir;
7344     else
7345       MovDir[x][y] = back_dir;
7346
7347     xx = x + move_xy[MovDir[x][y]].dx;
7348     yy = y + move_xy[MovDir[x][y]].dy;
7349
7350     if (!IN_LEV_FIELD(xx, yy) ||
7351         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352       MovDir[x][y] = old_move_dir;
7353
7354     MovDelay[x][y] = 0;
7355   }
7356   else if (element == EL_DRAGON)
7357   {
7358     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7361     int rnd_value = 24;
7362     int rnd = RND(rnd_value);
7363
7364     if (can_move_on && rnd > rnd_value / 8)
7365       MovDir[x][y] = old_move_dir;
7366     else if (can_turn_left && can_turn_right)
7367       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368     else if (can_turn_left && rnd > rnd_value / 8)
7369       MovDir[x][y] = left_dir;
7370     else if (can_turn_right && rnd > rnd_value / 8)
7371       MovDir[x][y] = right_dir;
7372     else
7373       MovDir[x][y] = back_dir;
7374
7375     xx = x + move_xy[MovDir[x][y]].dx;
7376     yy = y + move_xy[MovDir[x][y]].dy;
7377
7378     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379       MovDir[x][y] = old_move_dir;
7380
7381     MovDelay[x][y] = 0;
7382   }
7383   else if (element == EL_MOLE)
7384   {
7385     boolean can_move_on =
7386       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7388                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7389     if (!can_move_on)
7390     {
7391       boolean can_turn_left =
7392         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393                               IS_AMOEBOID(Feld[left_x][left_y])));
7394
7395       boolean can_turn_right =
7396         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397                               IS_AMOEBOID(Feld[right_x][right_y])));
7398
7399       if (can_turn_left && can_turn_right)
7400         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401       else if (can_turn_left)
7402         MovDir[x][y] = left_dir;
7403       else
7404         MovDir[x][y] = right_dir;
7405     }
7406
7407     if (MovDir[x][y] != old_move_dir)
7408       MovDelay[x][y] = 9;
7409   }
7410   else if (element == EL_BALLOON)
7411   {
7412     MovDir[x][y] = game.wind_direction;
7413     MovDelay[x][y] = 0;
7414   }
7415   else if (element == EL_SPRING)
7416   {
7417 #if USE_NEW_SPRING_BUMPER
7418     if (MovDir[x][y] & MV_HORIZONTAL)
7419     {
7420       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7422       {
7423         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424         ResetGfxAnimation(move_x, move_y);
7425         TEST_DrawLevelField(move_x, move_y);
7426
7427         MovDir[x][y] = back_dir;
7428       }
7429       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431         MovDir[x][y] = MV_NONE;
7432     }
7433 #else
7434     if (MovDir[x][y] & MV_HORIZONTAL &&
7435         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437       MovDir[x][y] = MV_NONE;
7438 #endif
7439
7440     MovDelay[x][y] = 0;
7441   }
7442   else if (element == EL_ROBOT ||
7443            element == EL_SATELLITE ||
7444            element == EL_PENGUIN ||
7445            element == EL_EMC_ANDROID)
7446   {
7447     int attr_x = -1, attr_y = -1;
7448
7449     if (AllPlayersGone)
7450     {
7451       attr_x = ExitX;
7452       attr_y = ExitY;
7453     }
7454     else
7455     {
7456       int i;
7457
7458       for (i = 0; i < MAX_PLAYERS; i++)
7459       {
7460         struct PlayerInfo *player = &stored_player[i];
7461         int jx = player->jx, jy = player->jy;
7462
7463         if (!player->active)
7464           continue;
7465
7466         if (attr_x == -1 ||
7467             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7468         {
7469           attr_x = jx;
7470           attr_y = jy;
7471         }
7472       }
7473     }
7474
7475     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477          game.engine_version < VERSION_IDENT(3,1,0,0)))
7478     {
7479       attr_x = ZX;
7480       attr_y = ZY;
7481     }
7482
7483     if (element == EL_PENGUIN)
7484     {
7485       int i;
7486       static int xy[4][2] =
7487       {
7488         { 0, -1 },
7489         { -1, 0 },
7490         { +1, 0 },
7491         { 0, +1 }
7492       };
7493
7494       for (i = 0; i < NUM_DIRECTIONS; i++)
7495       {
7496         int ex = x + xy[i][0];
7497         int ey = y + xy[i][1];
7498
7499         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7503         {
7504           attr_x = ex;
7505           attr_y = ey;
7506           break;
7507         }
7508       }
7509     }
7510
7511     MovDir[x][y] = MV_NONE;
7512     if (attr_x < x)
7513       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514     else if (attr_x > x)
7515       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7516     if (attr_y < y)
7517       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518     else if (attr_y > y)
7519       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7520
7521     if (element == EL_ROBOT)
7522     {
7523       int newx, newy;
7524
7525       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527       Moving2Blocked(x, y, &newx, &newy);
7528
7529       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530         MovDelay[x][y] = 8 + 8 * !RND(3);
7531       else
7532         MovDelay[x][y] = 16;
7533     }
7534     else if (element == EL_PENGUIN)
7535     {
7536       int newx, newy;
7537
7538       MovDelay[x][y] = 1;
7539
7540       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7541       {
7542         boolean first_horiz = RND(2);
7543         int new_move_dir = MovDir[x][y];
7544
7545         MovDir[x][y] =
7546           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547         Moving2Blocked(x, y, &newx, &newy);
7548
7549         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7550           return;
7551
7552         MovDir[x][y] =
7553           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554         Moving2Blocked(x, y, &newx, &newy);
7555
7556         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7557           return;
7558
7559         MovDir[x][y] = old_move_dir;
7560         return;
7561       }
7562     }
7563     else if (element == EL_SATELLITE)
7564     {
7565       int newx, newy;
7566
7567       MovDelay[x][y] = 1;
7568
7569       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7570       {
7571         boolean first_horiz = RND(2);
7572         int new_move_dir = MovDir[x][y];
7573
7574         MovDir[x][y] =
7575           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576         Moving2Blocked(x, y, &newx, &newy);
7577
7578         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7579           return;
7580
7581         MovDir[x][y] =
7582           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583         Moving2Blocked(x, y, &newx, &newy);
7584
7585         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7586           return;
7587
7588         MovDir[x][y] = old_move_dir;
7589         return;
7590       }
7591     }
7592     else if (element == EL_EMC_ANDROID)
7593     {
7594       static int check_pos[16] =
7595       {
7596         -1,             /*  0 => (invalid)          */
7597         7,              /*  1 => MV_LEFT            */
7598         3,              /*  2 => MV_RIGHT           */
7599         -1,             /*  3 => (invalid)          */
7600         1,              /*  4 =>            MV_UP   */
7601         0,              /*  5 => MV_LEFT  | MV_UP   */
7602         2,              /*  6 => MV_RIGHT | MV_UP   */
7603         -1,             /*  7 => (invalid)          */
7604         5,              /*  8 =>            MV_DOWN */
7605         6,              /*  9 => MV_LEFT  | MV_DOWN */
7606         4,              /* 10 => MV_RIGHT | MV_DOWN */
7607         -1,             /* 11 => (invalid)          */
7608         -1,             /* 12 => (invalid)          */
7609         -1,             /* 13 => (invalid)          */
7610         -1,             /* 14 => (invalid)          */
7611         -1,             /* 15 => (invalid)          */
7612       };
7613       static struct
7614       {
7615         int dx, dy;
7616         int dir;
7617       } check_xy[8] =
7618       {
7619         { -1, -1,       MV_LEFT  | MV_UP   },
7620         {  0, -1,                  MV_UP   },
7621         { +1, -1,       MV_RIGHT | MV_UP   },
7622         { +1,  0,       MV_RIGHT           },
7623         { +1, +1,       MV_RIGHT | MV_DOWN },
7624         {  0, +1,                  MV_DOWN },
7625         { -1, +1,       MV_LEFT  | MV_DOWN },
7626         { -1,  0,       MV_LEFT            },
7627       };
7628       int start_pos, check_order;
7629       boolean can_clone = FALSE;
7630       int i;
7631
7632       /* check if there is any free field around current position */
7633       for (i = 0; i < 8; i++)
7634       {
7635         int newx = x + check_xy[i].dx;
7636         int newy = y + check_xy[i].dy;
7637
7638         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7639         {
7640           can_clone = TRUE;
7641
7642           break;
7643         }
7644       }
7645
7646       if (can_clone)            /* randomly find an element to clone */
7647       {
7648         can_clone = FALSE;
7649
7650         start_pos = check_pos[RND(8)];
7651         check_order = (RND(2) ? -1 : +1);
7652
7653         for (i = 0; i < 8; i++)
7654         {
7655           int pos_raw = start_pos + i * check_order;
7656           int pos = (pos_raw + 8) % 8;
7657           int newx = x + check_xy[pos].dx;
7658           int newy = y + check_xy[pos].dy;
7659
7660           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7661           {
7662             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7664
7665             Store[x][y] = Feld[newx][newy];
7666
7667             can_clone = TRUE;
7668
7669             break;
7670           }
7671         }
7672       }
7673
7674       if (can_clone)            /* randomly find a direction to move */
7675       {
7676         can_clone = FALSE;
7677
7678         start_pos = check_pos[RND(8)];
7679         check_order = (RND(2) ? -1 : +1);
7680
7681         for (i = 0; i < 8; i++)
7682         {
7683           int pos_raw = start_pos + i * check_order;
7684           int pos = (pos_raw + 8) % 8;
7685           int newx = x + check_xy[pos].dx;
7686           int newy = y + check_xy[pos].dy;
7687           int new_move_dir = check_xy[pos].dir;
7688
7689           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7690           {
7691             MovDir[x][y] = new_move_dir;
7692             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7693
7694             can_clone = TRUE;
7695
7696             break;
7697           }
7698         }
7699       }
7700
7701       if (can_clone)            /* cloning and moving successful */
7702         return;
7703
7704       /* cannot clone -- try to move towards player */
7705
7706       start_pos = check_pos[MovDir[x][y] & 0x0f];
7707       check_order = (RND(2) ? -1 : +1);
7708
7709       for (i = 0; i < 3; i++)
7710       {
7711         /* first check start_pos, then previous/next or (next/previous) pos */
7712         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713         int pos = (pos_raw + 8) % 8;
7714         int newx = x + check_xy[pos].dx;
7715         int newy = y + check_xy[pos].dy;
7716         int new_move_dir = check_xy[pos].dir;
7717
7718         if (IS_PLAYER(newx, newy))
7719           break;
7720
7721         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7722         {
7723           MovDir[x][y] = new_move_dir;
7724           MovDelay[x][y] = level.android_move_time * 8 + 1;
7725
7726           break;
7727         }
7728       }
7729     }
7730   }
7731   else if (move_pattern == MV_TURNING_LEFT ||
7732            move_pattern == MV_TURNING_RIGHT ||
7733            move_pattern == MV_TURNING_LEFT_RIGHT ||
7734            move_pattern == MV_TURNING_RIGHT_LEFT ||
7735            move_pattern == MV_TURNING_RANDOM ||
7736            move_pattern == MV_ALL_DIRECTIONS)
7737   {
7738     boolean can_turn_left =
7739       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740     boolean can_turn_right =
7741       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7742
7743     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7744       return;
7745
7746     if (move_pattern == MV_TURNING_LEFT)
7747       MovDir[x][y] = left_dir;
7748     else if (move_pattern == MV_TURNING_RIGHT)
7749       MovDir[x][y] = right_dir;
7750     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754     else if (move_pattern == MV_TURNING_RANDOM)
7755       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756                       can_turn_right && !can_turn_left ? right_dir :
7757                       RND(2) ? left_dir : right_dir);
7758     else if (can_turn_left && can_turn_right)
7759       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760     else if (can_turn_left)
7761       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762     else if (can_turn_right)
7763       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7764     else
7765       MovDir[x][y] = back_dir;
7766
7767     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768   }
7769   else if (move_pattern == MV_HORIZONTAL ||
7770            move_pattern == MV_VERTICAL)
7771   {
7772     if (move_pattern & old_move_dir)
7773       MovDir[x][y] = back_dir;
7774     else if (move_pattern == MV_HORIZONTAL)
7775       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776     else if (move_pattern == MV_VERTICAL)
7777       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7778
7779     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7780   }
7781   else if (move_pattern & MV_ANY_DIRECTION)
7782   {
7783     MovDir[x][y] = move_pattern;
7784     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern & MV_WIND_DIRECTION)
7787   {
7788     MovDir[x][y] = game.wind_direction;
7789     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7790   }
7791   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7792   {
7793     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794       MovDir[x][y] = left_dir;
7795     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796       MovDir[x][y] = right_dir;
7797
7798     if (MovDir[x][y] != old_move_dir)
7799       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7800   }
7801   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7802   {
7803     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804       MovDir[x][y] = right_dir;
7805     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806       MovDir[x][y] = left_dir;
7807
7808     if (MovDir[x][y] != old_move_dir)
7809       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7810   }
7811   else if (move_pattern == MV_TOWARDS_PLAYER ||
7812            move_pattern == MV_AWAY_FROM_PLAYER)
7813   {
7814     int attr_x = -1, attr_y = -1;
7815     int newx, newy;
7816     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7817
7818     if (AllPlayersGone)
7819     {
7820       attr_x = ExitX;
7821       attr_y = ExitY;
7822     }
7823     else
7824     {
7825       int i;
7826
7827       for (i = 0; i < MAX_PLAYERS; i++)
7828       {
7829         struct PlayerInfo *player = &stored_player[i];
7830         int jx = player->jx, jy = player->jy;
7831
7832         if (!player->active)
7833           continue;
7834
7835         if (attr_x == -1 ||
7836             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7837         {
7838           attr_x = jx;
7839           attr_y = jy;
7840         }
7841       }
7842     }
7843
7844     MovDir[x][y] = MV_NONE;
7845     if (attr_x < x)
7846       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847     else if (attr_x > x)
7848       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7849     if (attr_y < y)
7850       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851     else if (attr_y > y)
7852       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7853
7854     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7855
7856     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7857     {
7858       boolean first_horiz = RND(2);
7859       int new_move_dir = MovDir[x][y];
7860
7861       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7862       {
7863         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7865
7866         return;
7867       }
7868
7869       MovDir[x][y] =
7870         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871       Moving2Blocked(x, y, &newx, &newy);
7872
7873       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7874         return;
7875
7876       MovDir[x][y] =
7877         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878       Moving2Blocked(x, y, &newx, &newy);
7879
7880       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7881         return;
7882
7883       MovDir[x][y] = old_move_dir;
7884     }
7885   }
7886   else if (move_pattern == MV_WHEN_PUSHED ||
7887            move_pattern == MV_WHEN_DROPPED)
7888   {
7889     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890       MovDir[x][y] = MV_NONE;
7891
7892     MovDelay[x][y] = 0;
7893   }
7894   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7895   {
7896     static int test_xy[7][2] =
7897     {
7898       { 0, -1 },
7899       { -1, 0 },
7900       { +1, 0 },
7901       { 0, +1 },
7902       { 0, -1 },
7903       { -1, 0 },
7904       { +1, 0 },
7905     };
7906     static int test_dir[7] =
7907     {
7908       MV_UP,
7909       MV_LEFT,
7910       MV_RIGHT,
7911       MV_DOWN,
7912       MV_UP,
7913       MV_LEFT,
7914       MV_RIGHT,
7915     };
7916     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917     int move_preference = -1000000;     /* start with very low preference */
7918     int new_move_dir = MV_NONE;
7919     int start_test = RND(4);
7920     int i;
7921
7922     for (i = 0; i < NUM_DIRECTIONS; i++)
7923     {
7924       int move_dir = test_dir[start_test + i];
7925       int move_dir_preference;
7926
7927       xx = x + test_xy[start_test + i][0];
7928       yy = y + test_xy[start_test + i][1];
7929
7930       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7932       {
7933         new_move_dir = move_dir;
7934
7935         break;
7936       }
7937
7938       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7939         continue;
7940
7941       move_dir_preference = -1 * RunnerVisit[xx][yy];
7942       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943         move_dir_preference = PlayerVisit[xx][yy];
7944
7945       if (move_dir_preference > move_preference)
7946       {
7947         /* prefer field that has not been visited for the longest time */
7948         move_preference = move_dir_preference;
7949         new_move_dir = move_dir;
7950       }
7951       else if (move_dir_preference == move_preference &&
7952                move_dir == old_move_dir)
7953       {
7954         /* prefer last direction when all directions are preferred equally */
7955         move_preference = move_dir_preference;
7956         new_move_dir = move_dir;
7957       }
7958     }
7959
7960     MovDir[x][y] = new_move_dir;
7961     if (old_move_dir != new_move_dir)
7962       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7963   }
7964 }
7965
7966 static void TurnRound(int x, int y)
7967 {
7968   int direction = MovDir[x][y];
7969
7970   TurnRoundExt(x, y);
7971
7972   GfxDir[x][y] = MovDir[x][y];
7973
7974   if (direction != MovDir[x][y])
7975     GfxFrame[x][y] = 0;
7976
7977   if (MovDelay[x][y])
7978     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7979
7980   ResetGfxFrame(x, y, FALSE);
7981 }
7982
7983 static boolean JustBeingPushed(int x, int y)
7984 {
7985   int i;
7986
7987   for (i = 0; i < MAX_PLAYERS; i++)
7988   {
7989     struct PlayerInfo *player = &stored_player[i];
7990
7991     if (player->active && player->is_pushing && player->MovPos)
7992     {
7993       int next_jx = player->jx + (player->jx - player->last_jx);
7994       int next_jy = player->jy + (player->jy - player->last_jy);
7995
7996       if (x == next_jx && y == next_jy)
7997         return TRUE;
7998     }
7999   }
8000
8001   return FALSE;
8002 }
8003
8004 void StartMoving(int x, int y)
8005 {
8006   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
8007   int element = Feld[x][y];
8008
8009   if (Stop[x][y])
8010     return;
8011
8012   if (MovDelay[x][y] == 0)
8013     GfxAction[x][y] = ACTION_DEFAULT;
8014
8015   if (CAN_FALL(element) && y < lev_fieldy - 1)
8016   {
8017     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8018         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019       if (JustBeingPushed(x, y))
8020         return;
8021
8022     if (element == EL_QUICKSAND_FULL)
8023     {
8024       if (IS_FREE(x, y + 1))
8025       {
8026         InitMovingField(x, y, MV_DOWN);
8027         started_moving = TRUE;
8028
8029         Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032           Store[x][y] = EL_ROCK;
8033 #else
8034         Store[x][y] = EL_ROCK;
8035 #endif
8036
8037         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8038       }
8039       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8040       {
8041         if (!MovDelay[x][y])
8042         {
8043           MovDelay[x][y] = TILEY + 1;
8044
8045           ResetGfxAnimation(x, y);
8046           ResetGfxAnimation(x, y + 1);
8047         }
8048
8049         if (MovDelay[x][y])
8050         {
8051           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8053
8054           MovDelay[x][y]--;
8055           if (MovDelay[x][y])
8056             return;
8057         }
8058
8059         Feld[x][y] = EL_QUICKSAND_EMPTY;
8060         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061         Store[x][y + 1] = Store[x][y];
8062         Store[x][y] = 0;
8063
8064         PlayLevelSoundAction(x, y, ACTION_FILLING);
8065       }
8066       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         if (!MovDelay[x][y])
8069         {
8070           MovDelay[x][y] = TILEY + 1;
8071
8072           ResetGfxAnimation(x, y);
8073           ResetGfxAnimation(x, y + 1);
8074         }
8075
8076         if (MovDelay[x][y])
8077         {
8078           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8080
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Feld[x][y] = EL_QUICKSAND_EMPTY;
8087         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088         Store[x][y + 1] = Store[x][y];
8089         Store[x][y] = 0;
8090
8091         PlayLevelSoundAction(x, y, ACTION_FILLING);
8092       }
8093     }
8094     else if (element == EL_QUICKSAND_FAST_FULL)
8095     {
8096       if (IS_FREE(x, y + 1))
8097       {
8098         InitMovingField(x, y, MV_DOWN);
8099         started_moving = TRUE;
8100
8101         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104           Store[x][y] = EL_ROCK;
8105 #else
8106         Store[x][y] = EL_ROCK;
8107 #endif
8108
8109         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8110       }
8111       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8112       {
8113         if (!MovDelay[x][y])
8114         {
8115           MovDelay[x][y] = TILEY + 1;
8116
8117           ResetGfxAnimation(x, y);
8118           ResetGfxAnimation(x, y + 1);
8119         }
8120
8121         if (MovDelay[x][y])
8122         {
8123           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8125
8126           MovDelay[x][y]--;
8127           if (MovDelay[x][y])
8128             return;
8129         }
8130
8131         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133         Store[x][y + 1] = Store[x][y];
8134         Store[x][y] = 0;
8135
8136         PlayLevelSoundAction(x, y, ACTION_FILLING);
8137       }
8138       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8139       {
8140         if (!MovDelay[x][y])
8141         {
8142           MovDelay[x][y] = TILEY + 1;
8143
8144           ResetGfxAnimation(x, y);
8145           ResetGfxAnimation(x, y + 1);
8146         }
8147
8148         if (MovDelay[x][y])
8149         {
8150           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8152
8153           MovDelay[x][y]--;
8154           if (MovDelay[x][y])
8155             return;
8156         }
8157
8158         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160         Store[x][y + 1] = Store[x][y];
8161         Store[x][y] = 0;
8162
8163         PlayLevelSoundAction(x, y, ACTION_FILLING);
8164       }
8165     }
8166     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8168     {
8169       InitMovingField(x, y, MV_DOWN);
8170       started_moving = TRUE;
8171
8172       Feld[x][y] = EL_QUICKSAND_FILLING;
8173       Store[x][y] = element;
8174
8175       PlayLevelSoundAction(x, y, ACTION_FILLING);
8176     }
8177     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8179     {
8180       InitMovingField(x, y, MV_DOWN);
8181       started_moving = TRUE;
8182
8183       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184       Store[x][y] = element;
8185
8186       PlayLevelSoundAction(x, y, ACTION_FILLING);
8187     }
8188     else if (element == EL_MAGIC_WALL_FULL)
8189     {
8190       if (IS_FREE(x, y + 1))
8191       {
8192         InitMovingField(x, y, MV_DOWN);
8193         started_moving = TRUE;
8194
8195         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196         Store[x][y] = EL_CHANGED(Store[x][y]);
8197       }
8198       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8199       {
8200         if (!MovDelay[x][y])
8201           MovDelay[x][y] = TILEY / 4 + 1;
8202
8203         if (MovDelay[x][y])
8204         {
8205           MovDelay[x][y]--;
8206           if (MovDelay[x][y])
8207             return;
8208         }
8209
8210         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8213         Store[x][y] = 0;
8214       }
8215     }
8216     else if (element == EL_BD_MAGIC_WALL_FULL)
8217     {
8218       if (IS_FREE(x, y + 1))
8219       {
8220         InitMovingField(x, y, MV_DOWN);
8221         started_moving = TRUE;
8222
8223         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8225       }
8226       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8227       {
8228         if (!MovDelay[x][y])
8229           MovDelay[x][y] = TILEY / 4 + 1;
8230
8231         if (MovDelay[x][y])
8232         {
8233           MovDelay[x][y]--;
8234           if (MovDelay[x][y])
8235             return;
8236         }
8237
8238         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8241         Store[x][y] = 0;
8242       }
8243     }
8244     else if (element == EL_DC_MAGIC_WALL_FULL)
8245     {
8246       if (IS_FREE(x, y + 1))
8247       {
8248         InitMovingField(x, y, MV_DOWN);
8249         started_moving = TRUE;
8250
8251         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8253       }
8254       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8255       {
8256         if (!MovDelay[x][y])
8257           MovDelay[x][y] = TILEY / 4 + 1;
8258
8259         if (MovDelay[x][y])
8260         {
8261           MovDelay[x][y]--;
8262           if (MovDelay[x][y])
8263             return;
8264         }
8265
8266         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8269         Store[x][y] = 0;
8270       }
8271     }
8272     else if ((CAN_PASS_MAGIC_WALL(element) &&
8273               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275              (CAN_PASS_DC_MAGIC_WALL(element) &&
8276               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8277
8278     {
8279       InitMovingField(x, y, MV_DOWN);
8280       started_moving = TRUE;
8281
8282       Feld[x][y] =
8283         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285          EL_DC_MAGIC_WALL_FILLING);
8286       Store[x][y] = element;
8287     }
8288     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8289     {
8290       SplashAcid(x, y + 1);
8291
8292       InitMovingField(x, y, MV_DOWN);
8293       started_moving = TRUE;
8294
8295       Store[x][y] = EL_ACID;
8296     }
8297     else if (
8298 #if USE_FIX_IMPACT_COLLISION
8299              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8301 #else
8302              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8304 #endif
8305              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306               CAN_FALL(element) && WasJustFalling[x][y] &&
8307               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8308
8309              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311               (Feld[x][y + 1] == EL_BLOCKED)))
8312     {
8313       /* this is needed for a special case not covered by calling "Impact()"
8314          from "ContinueMoving()": if an element moves to a tile directly below
8315          another element which was just falling on that tile (which was empty
8316          in the previous frame), the falling element above would just stop
8317          instead of smashing the element below (in previous version, the above
8318          element was just checked for "moving" instead of "falling", resulting
8319          in incorrect smashes caused by horizontal movement of the above
8320          element; also, the case of the player being the element to smash was
8321          simply not covered here... :-/ ) */
8322
8323       CheckCollision[x][y] = 0;
8324       CheckImpact[x][y] = 0;
8325
8326       Impact(x, y);
8327     }
8328     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8329     {
8330       if (MovDir[x][y] == MV_NONE)
8331       {
8332         InitMovingField(x, y, MV_DOWN);
8333         started_moving = TRUE;
8334       }
8335     }
8336     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8337     {
8338       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339         MovDir[x][y] = MV_DOWN;
8340
8341       InitMovingField(x, y, MV_DOWN);
8342       started_moving = TRUE;
8343     }
8344     else if (element == EL_AMOEBA_DROP)
8345     {
8346       Feld[x][y] = EL_AMOEBA_GROWING;
8347       Store[x][y] = EL_AMOEBA_WET;
8348     }
8349     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8353     {
8354       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8355                                 (IS_FREE(x - 1, y + 1) ||
8356                                  Feld[x - 1][y + 1] == EL_ACID));
8357       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358                                 (IS_FREE(x + 1, y + 1) ||
8359                                  Feld[x + 1][y + 1] == EL_ACID));
8360       boolean can_fall_any  = (can_fall_left || can_fall_right);
8361       boolean can_fall_both = (can_fall_left && can_fall_right);
8362       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8363
8364 #if USE_NEW_ALL_SLIPPERY
8365       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8366       {
8367         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368           can_fall_right = FALSE;
8369         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370           can_fall_left = FALSE;
8371         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372           can_fall_right = FALSE;
8373         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374           can_fall_left = FALSE;
8375
8376         can_fall_any  = (can_fall_left || can_fall_right);
8377         can_fall_both = FALSE;
8378       }
8379 #else
8380       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8381       {
8382         if (slippery_type == SLIPPERY_ONLY_LEFT)
8383           can_fall_right = FALSE;
8384         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385           can_fall_left = FALSE;
8386         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387           can_fall_right = FALSE;
8388         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389           can_fall_left = FALSE;
8390
8391         can_fall_any  = (can_fall_left || can_fall_right);
8392         can_fall_both = (can_fall_left && can_fall_right);
8393       }
8394 #endif
8395
8396 #if USE_NEW_ALL_SLIPPERY
8397 #else
8398 #if USE_NEW_SP_SLIPPERY
8399       /* !!! better use the same properties as for custom elements here !!! */
8400       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8402       {
8403         can_fall_right = FALSE;         /* slip down on left side */
8404         can_fall_both = FALSE;
8405       }
8406 #endif
8407 #endif
8408
8409 #if USE_NEW_ALL_SLIPPERY
8410       if (can_fall_both)
8411       {
8412         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413           can_fall_right = FALSE;       /* slip down on left side */
8414         else
8415           can_fall_left = !(can_fall_right = RND(2));
8416
8417         can_fall_both = FALSE;
8418       }
8419 #else
8420       if (can_fall_both)
8421       {
8422         if (game.emulation == EMU_BOULDERDASH ||
8423             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424           can_fall_right = FALSE;       /* slip down on left side */
8425         else
8426           can_fall_left = !(can_fall_right = RND(2));
8427
8428         can_fall_both = FALSE;
8429       }
8430 #endif
8431
8432       if (can_fall_any)
8433       {
8434         /* if not determined otherwise, prefer left side for slipping down */
8435         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436         started_moving = TRUE;
8437       }
8438     }
8439 #if 0
8440     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8441 #else
8442     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8443 #endif
8444     {
8445       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8446       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448       int belt_dir = game.belt_dir[belt_nr];
8449
8450       if ((belt_dir == MV_LEFT  && left_is_free) ||
8451           (belt_dir == MV_RIGHT && right_is_free))
8452       {
8453         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8454
8455         InitMovingField(x, y, belt_dir);
8456         started_moving = TRUE;
8457
8458         Pushed[x][y] = TRUE;
8459         Pushed[nextx][y] = TRUE;
8460
8461         GfxAction[x][y] = ACTION_DEFAULT;
8462       }
8463       else
8464       {
8465         MovDir[x][y] = 0;       /* if element was moving, stop it */
8466       }
8467     }
8468   }
8469
8470   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8471 #if 0
8472   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8473 #else
8474   if (CAN_MOVE(element) && !started_moving)
8475 #endif
8476   {
8477     int move_pattern = element_info[element].move_pattern;
8478     int newx, newy;
8479
8480 #if 0
8481 #if DEBUG
8482     if (MovDir[x][y] == MV_NONE)
8483     {
8484       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485              x, y, element, element_info[element].token_name);
8486       printf("StartMoving(): This should never happen!\n");
8487     }
8488 #endif
8489 #endif
8490
8491     Moving2Blocked(x, y, &newx, &newy);
8492
8493     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8494       return;
8495
8496     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8498     {
8499       WasJustMoving[x][y] = 0;
8500       CheckCollision[x][y] = 0;
8501
8502       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8503
8504       if (Feld[x][y] != element)        /* element has changed */
8505         return;
8506     }
8507
8508     if (!MovDelay[x][y])        /* start new movement phase */
8509     {
8510       /* all objects that can change their move direction after each step
8511          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8512
8513       if (element != EL_YAMYAM &&
8514           element != EL_DARK_YAMYAM &&
8515           element != EL_PACMAN &&
8516           !(move_pattern & MV_ANY_DIRECTION) &&
8517           move_pattern != MV_TURNING_LEFT &&
8518           move_pattern != MV_TURNING_RIGHT &&
8519           move_pattern != MV_TURNING_LEFT_RIGHT &&
8520           move_pattern != MV_TURNING_RIGHT_LEFT &&
8521           move_pattern != MV_TURNING_RANDOM)
8522       {
8523         TurnRound(x, y);
8524
8525         if (MovDelay[x][y] && (element == EL_BUG ||
8526                                element == EL_SPACESHIP ||
8527                                element == EL_SP_SNIKSNAK ||
8528                                element == EL_SP_ELECTRON ||
8529                                element == EL_MOLE))
8530           TEST_DrawLevelField(x, y);
8531       }
8532     }
8533
8534     if (MovDelay[x][y])         /* wait some time before next movement */
8535     {
8536       MovDelay[x][y]--;
8537
8538       if (element == EL_ROBOT ||
8539           element == EL_YAMYAM ||
8540           element == EL_DARK_YAMYAM)
8541       {
8542         DrawLevelElementAnimationIfNeeded(x, y, element);
8543         PlayLevelSoundAction(x, y, ACTION_WAITING);
8544       }
8545       else if (element == EL_SP_ELECTRON)
8546         DrawLevelElementAnimationIfNeeded(x, y, element);
8547       else if (element == EL_DRAGON)
8548       {
8549         int i;
8550         int dir = MovDir[x][y];
8551         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8553         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8554                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8555                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8556                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8558
8559         GfxAction[x][y] = ACTION_ATTACKING;
8560
8561         if (IS_PLAYER(x, y))
8562           DrawPlayerField(x, y);
8563         else
8564           TEST_DrawLevelField(x, y);
8565
8566         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8567
8568         for (i = 1; i <= 3; i++)
8569         {
8570           int xx = x + i * dx;
8571           int yy = y + i * dy;
8572           int sx = SCREENX(xx);
8573           int sy = SCREENY(yy);
8574           int flame_graphic = graphic + (i - 1);
8575
8576           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8577             break;
8578
8579           if (MovDelay[x][y])
8580           {
8581             int flamed = MovingOrBlocked2Element(xx, yy);
8582
8583             /* !!! */
8584 #if 0
8585             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8586               Bang(xx, yy);
8587             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588               RemoveMovingField(xx, yy);
8589             else
8590               RemoveField(xx, yy);
8591 #else
8592             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8593               Bang(xx, yy);
8594             else
8595               RemoveMovingField(xx, yy);
8596 #endif
8597
8598             ChangeDelay[xx][yy] = 0;
8599
8600             Feld[xx][yy] = EL_FLAMES;
8601
8602             if (IN_SCR_FIELD(sx, sy))
8603             {
8604               TEST_DrawLevelFieldCrumbled(xx, yy);
8605               DrawGraphic(sx, sy, flame_graphic, frame);
8606             }
8607           }
8608           else
8609           {
8610             if (Feld[xx][yy] == EL_FLAMES)
8611               Feld[xx][yy] = EL_EMPTY;
8612             TEST_DrawLevelField(xx, yy);
8613           }
8614         }
8615       }
8616
8617       if (MovDelay[x][y])       /* element still has to wait some time */
8618       {
8619         PlayLevelSoundAction(x, y, ACTION_WAITING);
8620
8621         return;
8622       }
8623     }
8624
8625     /* now make next step */
8626
8627     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8628
8629     if (DONT_COLLIDE_WITH(element) &&
8630         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631         !PLAYER_ENEMY_PROTECTED(newx, newy))
8632     {
8633       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8634
8635       return;
8636     }
8637
8638     else if (CAN_MOVE_INTO_ACID(element) &&
8639              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641              (MovDir[x][y] == MV_DOWN ||
8642               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8643     {
8644       SplashAcid(newx, newy);
8645       Store[x][y] = EL_ACID;
8646     }
8647     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8648     {
8649       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8653       {
8654         RemoveField(x, y);
8655         TEST_DrawLevelField(x, y);
8656
8657         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8660
8661         local_player->friends_still_needed--;
8662         if (!local_player->friends_still_needed &&
8663             !local_player->GameOver && AllPlayersGone)
8664           PlayerWins(local_player);
8665
8666         return;
8667       }
8668       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8669       {
8670         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671           TEST_DrawLevelField(newx, newy);
8672         else
8673           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8674       }
8675       else if (!IS_FREE(newx, newy))
8676       {
8677         GfxAction[x][y] = ACTION_WAITING;
8678
8679         if (IS_PLAYER(x, y))
8680           DrawPlayerField(x, y);
8681         else
8682           TEST_DrawLevelField(x, y);
8683
8684         return;
8685       }
8686     }
8687     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8688     {
8689       if (IS_FOOD_PIG(Feld[newx][newy]))
8690       {
8691         if (IS_MOVING(newx, newy))
8692           RemoveMovingField(newx, newy);
8693         else
8694         {
8695           Feld[newx][newy] = EL_EMPTY;
8696           TEST_DrawLevelField(newx, newy);
8697         }
8698
8699         PlayLevelSound(x, y, SND_PIG_DIGGING);
8700       }
8701       else if (!IS_FREE(newx, newy))
8702       {
8703         if (IS_PLAYER(x, y))
8704           DrawPlayerField(x, y);
8705         else
8706           TEST_DrawLevelField(x, y);
8707
8708         return;
8709       }
8710     }
8711     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8712     {
8713       if (Store[x][y] != EL_EMPTY)
8714       {
8715         boolean can_clone = FALSE;
8716         int xx, yy;
8717
8718         /* check if element to clone is still there */
8719         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8720         {
8721           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8722           {
8723             can_clone = TRUE;
8724
8725             break;
8726           }
8727         }
8728
8729         /* cannot clone or target field not free anymore -- do not clone */
8730         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731           Store[x][y] = EL_EMPTY;
8732       }
8733
8734       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8735       {
8736         if (IS_MV_DIAGONAL(MovDir[x][y]))
8737         {
8738           int diagonal_move_dir = MovDir[x][y];
8739           int stored = Store[x][y];
8740           int change_delay = 8;
8741           int graphic;
8742
8743           /* android is moving diagonally */
8744
8745           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8746
8747           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748           GfxElement[x][y] = EL_EMC_ANDROID;
8749           GfxAction[x][y] = ACTION_SHRINKING;
8750           GfxDir[x][y] = diagonal_move_dir;
8751           ChangeDelay[x][y] = change_delay;
8752
8753           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8754                                    GfxDir[x][y]);
8755
8756           DrawLevelGraphicAnimation(x, y, graphic);
8757           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8758
8759           if (Feld[newx][newy] == EL_ACID)
8760           {
8761             SplashAcid(newx, newy);
8762
8763             return;
8764           }
8765
8766           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8767
8768           Store[newx][newy] = EL_EMC_ANDROID;
8769           GfxElement[newx][newy] = EL_EMC_ANDROID;
8770           GfxAction[newx][newy] = ACTION_GROWING;
8771           GfxDir[newx][newy] = diagonal_move_dir;
8772           ChangeDelay[newx][newy] = change_delay;
8773
8774           graphic = el_act_dir2img(GfxElement[newx][newy],
8775                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8776
8777           DrawLevelGraphicAnimation(newx, newy, graphic);
8778           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8779
8780           return;
8781         }
8782         else
8783         {
8784           Feld[newx][newy] = EL_EMPTY;
8785           TEST_DrawLevelField(newx, newy);
8786
8787           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8788         }
8789       }
8790       else if (!IS_FREE(newx, newy))
8791       {
8792 #if 0
8793         if (IS_PLAYER(x, y))
8794           DrawPlayerField(x, y);
8795         else
8796           TEST_DrawLevelField(x, y);
8797 #endif
8798
8799         return;
8800       }
8801     }
8802     else if (IS_CUSTOM_ELEMENT(element) &&
8803              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8804     {
8805 #if 1
8806       if (!DigFieldByCE(newx, newy, element))
8807         return;
8808 #else
8809       int new_element = Feld[newx][newy];
8810
8811       if (!IS_FREE(newx, newy))
8812       {
8813         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8815                       ACTION_BREAKING);
8816
8817         /* no element can dig solid indestructible elements */
8818         if (IS_INDESTRUCTIBLE(new_element) &&
8819             !IS_DIGGABLE(new_element) &&
8820             !IS_COLLECTIBLE(new_element))
8821           return;
8822
8823         if (AmoebaNr[newx][newy] &&
8824             (new_element == EL_AMOEBA_FULL ||
8825              new_element == EL_BD_AMOEBA ||
8826              new_element == EL_AMOEBA_GROWING))
8827         {
8828           AmoebaCnt[AmoebaNr[newx][newy]]--;
8829           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8830         }
8831
8832         if (IS_MOVING(newx, newy))
8833           RemoveMovingField(newx, newy);
8834         else
8835         {
8836           RemoveField(newx, newy);
8837           TEST_DrawLevelField(newx, newy);
8838         }
8839
8840         /* if digged element was about to explode, prevent the explosion */
8841         ExplodeField[newx][newy] = EX_TYPE_NONE;
8842
8843         PlayLevelSoundAction(x, y, action);
8844       }
8845
8846       Store[newx][newy] = EL_EMPTY;
8847
8848 #if 1
8849       /* this makes it possible to leave the removed element again */
8850       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851         Store[newx][newy] = new_element;
8852 #else
8853       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8854       {
8855         int move_leave_element = element_info[element].move_leave_element;
8856
8857         /* this makes it possible to leave the removed element again */
8858         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859                              new_element : move_leave_element);
8860       }
8861 #endif
8862
8863 #endif
8864
8865       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8866       {
8867         RunnerVisit[x][y] = FrameCounter;
8868         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8869       }
8870     }
8871     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8872     {
8873       if (!IS_FREE(newx, newy))
8874       {
8875         if (IS_PLAYER(x, y))
8876           DrawPlayerField(x, y);
8877         else
8878           TEST_DrawLevelField(x, y);
8879
8880         return;
8881       }
8882       else
8883       {
8884         boolean wanna_flame = !RND(10);
8885         int dx = newx - x, dy = newy - y;
8886         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8892
8893         if ((wanna_flame ||
8894              IS_CLASSIC_ENEMY(element1) ||
8895              IS_CLASSIC_ENEMY(element2)) &&
8896             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897             element1 != EL_FLAMES && element2 != EL_FLAMES)
8898         {
8899           ResetGfxAnimation(x, y);
8900           GfxAction[x][y] = ACTION_ATTACKING;
8901
8902           if (IS_PLAYER(x, y))
8903             DrawPlayerField(x, y);
8904           else
8905             TEST_DrawLevelField(x, y);
8906
8907           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8908
8909           MovDelay[x][y] = 50;
8910
8911           /* !!! */
8912 #if 0
8913           RemoveField(newx, newy);
8914 #endif
8915           Feld[newx][newy] = EL_FLAMES;
8916           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8917           {
8918 #if 0
8919             RemoveField(newx1, newy1);
8920 #endif
8921             Feld[newx1][newy1] = EL_FLAMES;
8922           }
8923           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8924           {
8925 #if 0
8926             RemoveField(newx2, newy2);
8927 #endif
8928             Feld[newx2][newy2] = EL_FLAMES;
8929           }
8930
8931           return;
8932         }
8933       }
8934     }
8935     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936              Feld[newx][newy] == EL_DIAMOND)
8937     {
8938       if (IS_MOVING(newx, newy))
8939         RemoveMovingField(newx, newy);
8940       else
8941       {
8942         Feld[newx][newy] = EL_EMPTY;
8943         TEST_DrawLevelField(newx, newy);
8944       }
8945
8946       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8947     }
8948     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8950     {
8951       if (AmoebaNr[newx][newy])
8952       {
8953         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955             Feld[newx][newy] == EL_BD_AMOEBA)
8956           AmoebaCnt[AmoebaNr[newx][newy]]--;
8957       }
8958
8959 #if 0
8960       /* !!! test !!! */
8961       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8962       {
8963         RemoveMovingField(newx, newy);
8964       }
8965 #else
8966       if (IS_MOVING(newx, newy))
8967       {
8968         RemoveMovingField(newx, newy);
8969       }
8970 #endif
8971       else
8972       {
8973         Feld[newx][newy] = EL_EMPTY;
8974         TEST_DrawLevelField(newx, newy);
8975       }
8976
8977       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8978     }
8979     else if ((element == EL_PACMAN || element == EL_MOLE)
8980              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8981     {
8982       if (AmoebaNr[newx][newy])
8983       {
8984         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986             Feld[newx][newy] == EL_BD_AMOEBA)
8987           AmoebaCnt[AmoebaNr[newx][newy]]--;
8988       }
8989
8990       if (element == EL_MOLE)
8991       {
8992         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8994
8995         ResetGfxAnimation(x, y);
8996         GfxAction[x][y] = ACTION_DIGGING;
8997         TEST_DrawLevelField(x, y);
8998
8999         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
9000
9001         return;                         /* wait for shrinking amoeba */
9002       }
9003       else      /* element == EL_PACMAN */
9004       {
9005         Feld[newx][newy] = EL_EMPTY;
9006         TEST_DrawLevelField(newx, newy);
9007         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9008       }
9009     }
9010     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9013     {
9014       /* wait for shrinking amoeba to completely disappear */
9015       return;
9016     }
9017     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9018     {
9019       /* object was running against a wall */
9020
9021       TurnRound(x, y);
9022
9023 #if 0
9024       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025       if (move_pattern & MV_ANY_DIRECTION &&
9026           move_pattern == MovDir[x][y])
9027       {
9028         int blocking_element =
9029           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9030
9031         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9032                                  MovDir[x][y]);
9033
9034         element = Feld[x][y];   /* element might have changed */
9035       }
9036 #endif
9037
9038       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9039         DrawLevelElementAnimation(x, y, element);
9040
9041       if (DONT_TOUCH(element))
9042         TestIfBadThingTouchesPlayer(x, y);
9043
9044       return;
9045     }
9046
9047     InitMovingField(x, y, MovDir[x][y]);
9048
9049     PlayLevelSoundAction(x, y, ACTION_MOVING);
9050   }
9051
9052   if (MovDir[x][y])
9053     ContinueMoving(x, y);
9054 }
9055
9056 void ContinueMoving(int x, int y)
9057 {
9058   int element = Feld[x][y];
9059   struct ElementInfo *ei = &element_info[element];
9060   int direction = MovDir[x][y];
9061   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9063   int newx = x + dx, newy = y + dy;
9064   int stored = Store[x][y];
9065   int stored_new = Store[newx][newy];
9066   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9067   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068   boolean last_line = (newy == lev_fieldy - 1);
9069
9070   MovPos[x][y] += getElementMoveStepsize(x, y);
9071
9072   if (pushed_by_player) /* special case: moving object pushed by player */
9073     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9074
9075   if (ABS(MovPos[x][y]) < TILEX)
9076   {
9077 #if 0
9078     int ee = Feld[x][y];
9079     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9081
9082     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083            x, y, ABS(MovPos[x][y]),
9084            ee, gg, ff,
9085            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9086 #endif
9087
9088     TEST_DrawLevelField(x, y);
9089
9090     return;     /* element is still moving */
9091   }
9092
9093   /* element reached destination field */
9094
9095   Feld[x][y] = EL_EMPTY;
9096   Feld[newx][newy] = element;
9097   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9098
9099   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9100   {
9101     element = Feld[newx][newy] = EL_ACID;
9102   }
9103   else if (element == EL_MOLE)
9104   {
9105     Feld[x][y] = EL_SAND;
9106
9107     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9108   }
9109   else if (element == EL_QUICKSAND_FILLING)
9110   {
9111     element = Feld[newx][newy] = get_next_element(element);
9112     Store[newx][newy] = Store[x][y];
9113   }
9114   else if (element == EL_QUICKSAND_EMPTYING)
9115   {
9116     Feld[x][y] = get_next_element(element);
9117     element = Feld[newx][newy] = Store[x][y];
9118   }
9119   else if (element == EL_QUICKSAND_FAST_FILLING)
9120   {
9121     element = Feld[newx][newy] = get_next_element(element);
9122     Store[newx][newy] = Store[x][y];
9123   }
9124   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9125   {
9126     Feld[x][y] = get_next_element(element);
9127     element = Feld[newx][newy] = Store[x][y];
9128   }
9129   else if (element == EL_MAGIC_WALL_FILLING)
9130   {
9131     element = Feld[newx][newy] = get_next_element(element);
9132     if (!game.magic_wall_active)
9133       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134     Store[newx][newy] = Store[x][y];
9135   }
9136   else if (element == EL_MAGIC_WALL_EMPTYING)
9137   {
9138     Feld[x][y] = get_next_element(element);
9139     if (!game.magic_wall_active)
9140       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141     element = Feld[newx][newy] = Store[x][y];
9142
9143 #if USE_NEW_CUSTOM_VALUE
9144     InitField(newx, newy, FALSE);
9145 #endif
9146   }
9147   else if (element == EL_BD_MAGIC_WALL_FILLING)
9148   {
9149     element = Feld[newx][newy] = get_next_element(element);
9150     if (!game.magic_wall_active)
9151       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152     Store[newx][newy] = Store[x][y];
9153   }
9154   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9155   {
9156     Feld[x][y] = get_next_element(element);
9157     if (!game.magic_wall_active)
9158       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159     element = Feld[newx][newy] = Store[x][y];
9160
9161 #if USE_NEW_CUSTOM_VALUE
9162     InitField(newx, newy, FALSE);
9163 #endif
9164   }
9165   else if (element == EL_DC_MAGIC_WALL_FILLING)
9166   {
9167     element = Feld[newx][newy] = get_next_element(element);
9168     if (!game.magic_wall_active)
9169       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170     Store[newx][newy] = Store[x][y];
9171   }
9172   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9173   {
9174     Feld[x][y] = get_next_element(element);
9175     if (!game.magic_wall_active)
9176       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177     element = Feld[newx][newy] = Store[x][y];
9178
9179 #if USE_NEW_CUSTOM_VALUE
9180     InitField(newx, newy, FALSE);
9181 #endif
9182   }
9183   else if (element == EL_AMOEBA_DROPPING)
9184   {
9185     Feld[x][y] = get_next_element(element);
9186     element = Feld[newx][newy] = Store[x][y];
9187   }
9188   else if (element == EL_SOKOBAN_OBJECT)
9189   {
9190     if (Back[x][y])
9191       Feld[x][y] = Back[x][y];
9192
9193     if (Back[newx][newy])
9194       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9195
9196     Back[x][y] = Back[newx][newy] = 0;
9197   }
9198
9199   Store[x][y] = EL_EMPTY;
9200   MovPos[x][y] = 0;
9201   MovDir[x][y] = 0;
9202   MovDelay[x][y] = 0;
9203
9204   MovDelay[newx][newy] = 0;
9205
9206   if (CAN_CHANGE_OR_HAS_ACTION(element))
9207   {
9208     /* copy element change control values to new field */
9209     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210     ChangePage[newx][newy]  = ChangePage[x][y];
9211     ChangeCount[newx][newy] = ChangeCount[x][y];
9212     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9213   }
9214
9215 #if USE_NEW_CUSTOM_VALUE
9216   CustomValue[newx][newy] = CustomValue[x][y];
9217 #endif
9218
9219   ChangeDelay[x][y] = 0;
9220   ChangePage[x][y] = -1;
9221   ChangeCount[x][y] = 0;
9222   ChangeEvent[x][y] = -1;
9223
9224 #if USE_NEW_CUSTOM_VALUE
9225   CustomValue[x][y] = 0;
9226 #endif
9227
9228   /* copy animation control values to new field */
9229   GfxFrame[newx][newy]  = GfxFrame[x][y];
9230   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9231   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9232   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9233
9234   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9235
9236   /* some elements can leave other elements behind after moving */
9237 #if 1
9238   if (ei->move_leave_element != EL_EMPTY &&
9239       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9241 #else
9242   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9245 #endif
9246   {
9247     int move_leave_element = ei->move_leave_element;
9248
9249 #if 1
9250 #if 1
9251     /* this makes it possible to leave the removed element again */
9252     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9254 #else
9255     /* this makes it possible to leave the removed element again */
9256     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257       move_leave_element = stored;
9258 #endif
9259 #else
9260     /* this makes it possible to leave the removed element again */
9261     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263       move_leave_element = stored;
9264 #endif
9265
9266     Feld[x][y] = move_leave_element;
9267
9268     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269       MovDir[x][y] = direction;
9270
9271     InitField(x, y, FALSE);
9272
9273     if (GFX_CRUMBLED(Feld[x][y]))
9274       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9275
9276     if (ELEM_IS_PLAYER(move_leave_element))
9277       RelocatePlayer(x, y, move_leave_element);
9278   }
9279
9280   /* do this after checking for left-behind element */
9281   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9282
9283   if (!CAN_MOVE(element) ||
9284       (CAN_FALL(element) && direction == MV_DOWN &&
9285        (element == EL_SPRING ||
9286         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288     GfxDir[x][y] = MovDir[newx][newy] = 0;
9289
9290   TEST_DrawLevelField(x, y);
9291   TEST_DrawLevelField(newx, newy);
9292
9293   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9294
9295   /* prevent pushed element from moving on in pushed direction */
9296   if (pushed_by_player && CAN_MOVE(element) &&
9297       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298       !(element_info[element].move_pattern & direction))
9299     TurnRound(newx, newy);
9300
9301   /* prevent elements on conveyor belt from moving on in last direction */
9302   if (pushed_by_conveyor && CAN_FALL(element) &&
9303       direction & MV_HORIZONTAL)
9304     MovDir[newx][newy] = 0;
9305
9306   if (!pushed_by_player)
9307   {
9308     int nextx = newx + dx, nexty = newy + dy;
9309     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9310
9311     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9312
9313     if (CAN_FALL(element) && direction == MV_DOWN)
9314       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9315
9316     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9318
9319 #if USE_FIX_IMPACT_COLLISION
9320     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9322 #endif
9323   }
9324
9325   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9326   {
9327     TestIfBadThingTouchesPlayer(newx, newy);
9328     TestIfBadThingTouchesFriend(newx, newy);
9329
9330     if (!IS_CUSTOM_ELEMENT(element))
9331       TestIfBadThingTouchesOtherBadThing(newx, newy);
9332   }
9333   else if (element == EL_PENGUIN)
9334     TestIfFriendTouchesBadThing(newx, newy);
9335
9336   if (DONT_GET_HIT_BY(element))
9337   {
9338     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9339   }
9340
9341   /* give the player one last chance (one more frame) to move away */
9342   if (CAN_FALL(element) && direction == MV_DOWN &&
9343       (last_line || (!IS_FREE(x, newy + 1) &&
9344                      (!IS_PLAYER(x, newy + 1) ||
9345                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9346     Impact(x, newy);
9347
9348   if (pushed_by_player && !game.use_change_when_pushing_bug)
9349   {
9350     int push_side = MV_DIR_OPPOSITE(direction);
9351     struct PlayerInfo *player = PLAYERINFO(x, y);
9352
9353     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354                                player->index_bit, push_side);
9355     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356                                         player->index_bit, push_side);
9357   }
9358
9359   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9360     MovDelay[newx][newy] = 1;
9361
9362   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9363
9364   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9365
9366 #if 0
9367   if (ChangePage[newx][newy] != -1)             /* delayed change */
9368   {
9369     int page = ChangePage[newx][newy];
9370     struct ElementChangeInfo *change = &ei->change_page[page];
9371
9372     ChangePage[newx][newy] = -1;
9373
9374     if (change->can_change)
9375     {
9376       if (ChangeElement(newx, newy, element, page))
9377       {
9378         if (change->post_change_function)
9379           change->post_change_function(newx, newy);
9380       }
9381     }
9382
9383     if (change->has_action)
9384       ExecuteCustomElementAction(newx, newy, element, page);
9385   }
9386 #endif
9387
9388   TestIfElementHitsCustomElement(newx, newy, direction);
9389   TestIfPlayerTouchesCustomElement(newx, newy);
9390   TestIfElementTouchesCustomElement(newx, newy);
9391
9392   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395                              MV_DIR_OPPOSITE(direction));
9396 }
9397
9398 int AmoebeNachbarNr(int ax, int ay)
9399 {
9400   int i;
9401   int element = Feld[ax][ay];
9402   int group_nr = 0;
9403   static int xy[4][2] =
9404   {
9405     { 0, -1 },
9406     { -1, 0 },
9407     { +1, 0 },
9408     { 0, +1 }
9409   };
9410
9411   for (i = 0; i < NUM_DIRECTIONS; i++)
9412   {
9413     int x = ax + xy[i][0];
9414     int y = ay + xy[i][1];
9415
9416     if (!IN_LEV_FIELD(x, y))
9417       continue;
9418
9419     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420       group_nr = AmoebaNr[x][y];
9421   }
9422
9423   return group_nr;
9424 }
9425
9426 void AmoebenVereinigen(int ax, int ay)
9427 {
9428   int i, x, y, xx, yy;
9429   int new_group_nr = AmoebaNr[ax][ay];
9430   static int xy[4][2] =
9431   {
9432     { 0, -1 },
9433     { -1, 0 },
9434     { +1, 0 },
9435     { 0, +1 }
9436   };
9437
9438   if (new_group_nr == 0)
9439     return;
9440
9441   for (i = 0; i < NUM_DIRECTIONS; i++)
9442   {
9443     x = ax + xy[i][0];
9444     y = ay + xy[i][1];
9445
9446     if (!IN_LEV_FIELD(x, y))
9447       continue;
9448
9449     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450          Feld[x][y] == EL_BD_AMOEBA ||
9451          Feld[x][y] == EL_AMOEBA_DEAD) &&
9452         AmoebaNr[x][y] != new_group_nr)
9453     {
9454       int old_group_nr = AmoebaNr[x][y];
9455
9456       if (old_group_nr == 0)
9457         return;
9458
9459       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460       AmoebaCnt[old_group_nr] = 0;
9461       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462       AmoebaCnt2[old_group_nr] = 0;
9463
9464       SCAN_PLAYFIELD(xx, yy)
9465       {
9466         if (AmoebaNr[xx][yy] == old_group_nr)
9467           AmoebaNr[xx][yy] = new_group_nr;
9468       }
9469     }
9470   }
9471 }
9472
9473 void AmoebeUmwandeln(int ax, int ay)
9474 {
9475   int i, x, y;
9476
9477   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9478   {
9479     int group_nr = AmoebaNr[ax][ay];
9480
9481 #ifdef DEBUG
9482     if (group_nr == 0)
9483     {
9484       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485       printf("AmoebeUmwandeln(): This should never happen!\n");
9486       return;
9487     }
9488 #endif
9489
9490     SCAN_PLAYFIELD(x, y)
9491     {
9492       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9493       {
9494         AmoebaNr[x][y] = 0;
9495         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9496       }
9497     }
9498
9499     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500                             SND_AMOEBA_TURNING_TO_GEM :
9501                             SND_AMOEBA_TURNING_TO_ROCK));
9502     Bang(ax, ay);
9503   }
9504   else
9505   {
9506     static int xy[4][2] =
9507     {
9508       { 0, -1 },
9509       { -1, 0 },
9510       { +1, 0 },
9511       { 0, +1 }
9512     };
9513
9514     for (i = 0; i < NUM_DIRECTIONS; i++)
9515     {
9516       x = ax + xy[i][0];
9517       y = ay + xy[i][1];
9518
9519       if (!IN_LEV_FIELD(x, y))
9520         continue;
9521
9522       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9523       {
9524         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525                               SND_AMOEBA_TURNING_TO_GEM :
9526                               SND_AMOEBA_TURNING_TO_ROCK));
9527         Bang(x, y);
9528       }
9529     }
9530   }
9531 }
9532
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9534 {
9535   int x, y;
9536   int group_nr = AmoebaNr[ax][ay];
9537   boolean done = FALSE;
9538
9539 #ifdef DEBUG
9540   if (group_nr == 0)
9541   {
9542     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9544     return;
9545   }
9546 #endif
9547
9548   SCAN_PLAYFIELD(x, y)
9549   {
9550     if (AmoebaNr[x][y] == group_nr &&
9551         (Feld[x][y] == EL_AMOEBA_DEAD ||
9552          Feld[x][y] == EL_BD_AMOEBA ||
9553          Feld[x][y] == EL_AMOEBA_GROWING))
9554     {
9555       AmoebaNr[x][y] = 0;
9556       Feld[x][y] = new_element;
9557       InitField(x, y, FALSE);
9558       TEST_DrawLevelField(x, y);
9559       done = TRUE;
9560     }
9561   }
9562
9563   if (done)
9564     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9566                             SND_BD_AMOEBA_TURNING_TO_GEM));
9567 }
9568
9569 void AmoebeWaechst(int x, int y)
9570 {
9571   static unsigned int sound_delay = 0;
9572   static unsigned int sound_delay_value = 0;
9573
9574   if (!MovDelay[x][y])          /* start new growing cycle */
9575   {
9576     MovDelay[x][y] = 7;
9577
9578     if (DelayReached(&sound_delay, sound_delay_value))
9579     {
9580       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581       sound_delay_value = 30;
9582     }
9583   }
9584
9585   if (MovDelay[x][y])           /* wait some time before growing bigger */
9586   {
9587     MovDelay[x][y]--;
9588     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9589     {
9590       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591                                            6 - MovDelay[x][y]);
9592
9593       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9594     }
9595
9596     if (!MovDelay[x][y])
9597     {
9598       Feld[x][y] = Store[x][y];
9599       Store[x][y] = 0;
9600       TEST_DrawLevelField(x, y);
9601     }
9602   }
9603 }
9604
9605 void AmoebaDisappearing(int x, int y)
9606 {
9607   static unsigned int sound_delay = 0;
9608   static unsigned int sound_delay_value = 0;
9609
9610   if (!MovDelay[x][y])          /* start new shrinking cycle */
9611   {
9612     MovDelay[x][y] = 7;
9613
9614     if (DelayReached(&sound_delay, sound_delay_value))
9615       sound_delay_value = 30;
9616   }
9617
9618   if (MovDelay[x][y])           /* wait some time before shrinking */
9619   {
9620     MovDelay[x][y]--;
9621     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9622     {
9623       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624                                            6 - MovDelay[x][y]);
9625
9626       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9627     }
9628
9629     if (!MovDelay[x][y])
9630     {
9631       Feld[x][y] = EL_EMPTY;
9632       TEST_DrawLevelField(x, y);
9633
9634       /* don't let mole enter this field in this cycle;
9635          (give priority to objects falling to this field from above) */
9636       Stop[x][y] = TRUE;
9637     }
9638   }
9639 }
9640
9641 void AmoebeAbleger(int ax, int ay)
9642 {
9643   int i;
9644   int element = Feld[ax][ay];
9645   int graphic = el2img(element);
9646   int newax = ax, neway = ay;
9647   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648   static int xy[4][2] =
9649   {
9650     { 0, -1 },
9651     { -1, 0 },
9652     { +1, 0 },
9653     { 0, +1 }
9654   };
9655
9656   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9657   {
9658     Feld[ax][ay] = EL_AMOEBA_DEAD;
9659     TEST_DrawLevelField(ax, ay);
9660     return;
9661   }
9662
9663   if (IS_ANIMATED(graphic))
9664     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9665
9666   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9667     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9668
9669   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9670   {
9671     MovDelay[ax][ay]--;
9672     if (MovDelay[ax][ay])
9673       return;
9674   }
9675
9676   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9677   {
9678     int start = RND(4);
9679     int x = ax + xy[start][0];
9680     int y = ay + xy[start][1];
9681
9682     if (!IN_LEV_FIELD(x, y))
9683       return;
9684
9685     if (IS_FREE(x, y) ||
9686         CAN_GROW_INTO(Feld[x][y]) ||
9687         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9689     {
9690       newax = x;
9691       neway = y;
9692     }
9693
9694     if (newax == ax && neway == ay)
9695       return;
9696   }
9697   else                          /* normal or "filled" (BD style) amoeba */
9698   {
9699     int start = RND(4);
9700     boolean waiting_for_player = FALSE;
9701
9702     for (i = 0; i < NUM_DIRECTIONS; i++)
9703     {
9704       int j = (start + i) % 4;
9705       int x = ax + xy[j][0];
9706       int y = ay + xy[j][1];
9707
9708       if (!IN_LEV_FIELD(x, y))
9709         continue;
9710
9711       if (IS_FREE(x, y) ||
9712           CAN_GROW_INTO(Feld[x][y]) ||
9713           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9715       {
9716         newax = x;
9717         neway = y;
9718         break;
9719       }
9720       else if (IS_PLAYER(x, y))
9721         waiting_for_player = TRUE;
9722     }
9723
9724     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9725     {
9726       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9727       {
9728         Feld[ax][ay] = EL_AMOEBA_DEAD;
9729         TEST_DrawLevelField(ax, ay);
9730         AmoebaCnt[AmoebaNr[ax][ay]]--;
9731
9732         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9733         {
9734           if (element == EL_AMOEBA_FULL)
9735             AmoebeUmwandeln(ax, ay);
9736           else if (element == EL_BD_AMOEBA)
9737             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9738         }
9739       }
9740       return;
9741     }
9742     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9743     {
9744       /* amoeba gets larger by growing in some direction */
9745
9746       int new_group_nr = AmoebaNr[ax][ay];
9747
9748 #ifdef DEBUG
9749   if (new_group_nr == 0)
9750   {
9751     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752     printf("AmoebeAbleger(): This should never happen!\n");
9753     return;
9754   }
9755 #endif
9756
9757       AmoebaNr[newax][neway] = new_group_nr;
9758       AmoebaCnt[new_group_nr]++;
9759       AmoebaCnt2[new_group_nr]++;
9760
9761       /* if amoeba touches other amoeba(s) after growing, unify them */
9762       AmoebenVereinigen(newax, neway);
9763
9764       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9765       {
9766         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9767         return;
9768       }
9769     }
9770   }
9771
9772   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773       (neway == lev_fieldy - 1 && newax != ax))
9774   {
9775     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9776     Store[newax][neway] = element;
9777   }
9778   else if (neway == ay || element == EL_EMC_DRIPPER)
9779   {
9780     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9781
9782     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9783   }
9784   else
9785   {
9786     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9787     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788     Store[ax][ay] = EL_AMOEBA_DROP;
9789     ContinueMoving(ax, ay);
9790     return;
9791   }
9792
9793   TEST_DrawLevelField(newax, neway);
9794 }
9795
9796 void Life(int ax, int ay)
9797 {
9798   int x1, y1, x2, y2;
9799   int life_time = 40;
9800   int element = Feld[ax][ay];
9801   int graphic = el2img(element);
9802   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9803                          level.biomaze);
9804   boolean changed = FALSE;
9805
9806   if (IS_ANIMATED(graphic))
9807     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9808
9809   if (Stop[ax][ay])
9810     return;
9811
9812   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9813     MovDelay[ax][ay] = life_time;
9814
9815   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9816   {
9817     MovDelay[ax][ay]--;
9818     if (MovDelay[ax][ay])
9819       return;
9820   }
9821
9822   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9823   {
9824     int xx = ax+x1, yy = ay+y1;
9825     int nachbarn = 0;
9826
9827     if (!IN_LEV_FIELD(xx, yy))
9828       continue;
9829
9830     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9831     {
9832       int x = xx+x2, y = yy+y2;
9833
9834       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9835         continue;
9836
9837       if (((Feld[x][y] == element ||
9838             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9839            !Stop[x][y]) ||
9840           (IS_FREE(x, y) && Stop[x][y]))
9841         nachbarn++;
9842     }
9843
9844     if (xx == ax && yy == ay)           /* field in the middle */
9845     {
9846       if (nachbarn < life_parameter[0] ||
9847           nachbarn > life_parameter[1])
9848       {
9849         Feld[xx][yy] = EL_EMPTY;
9850         if (!Stop[xx][yy])
9851           TEST_DrawLevelField(xx, yy);
9852         Stop[xx][yy] = TRUE;
9853         changed = TRUE;
9854       }
9855     }
9856     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857     {                                   /* free border field */
9858       if (nachbarn >= life_parameter[2] &&
9859           nachbarn <= life_parameter[3])
9860       {
9861         Feld[xx][yy] = element;
9862         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9863         if (!Stop[xx][yy])
9864           TEST_DrawLevelField(xx, yy);
9865         Stop[xx][yy] = TRUE;
9866         changed = TRUE;
9867       }
9868     }
9869   }
9870
9871   if (changed)
9872     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873                    SND_GAME_OF_LIFE_GROWING);
9874 }
9875
9876 static void InitRobotWheel(int x, int y)
9877 {
9878   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9879 }
9880
9881 static void RunRobotWheel(int x, int y)
9882 {
9883   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9884 }
9885
9886 static void StopRobotWheel(int x, int y)
9887 {
9888   if (ZX == x && ZY == y)
9889   {
9890     ZX = ZY = -1;
9891
9892     game.robot_wheel_active = FALSE;
9893   }
9894 }
9895
9896 static void InitTimegateWheel(int x, int y)
9897 {
9898   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9899 }
9900
9901 static void RunTimegateWheel(int x, int y)
9902 {
9903   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9904 }
9905
9906 static void InitMagicBallDelay(int x, int y)
9907 {
9908 #if 1
9909   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9910 #else
9911   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9912 #endif
9913 }
9914
9915 static void ActivateMagicBall(int bx, int by)
9916 {
9917   int x, y;
9918
9919   if (level.ball_random)
9920   {
9921     int pos_border = RND(8);    /* select one of the eight border elements */
9922     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923     int xx = pos_content % 3;
9924     int yy = pos_content / 3;
9925
9926     x = bx - 1 + xx;
9927     y = by - 1 + yy;
9928
9929     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9931   }
9932   else
9933   {
9934     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9935     {
9936       int xx = x - bx + 1;
9937       int yy = y - by + 1;
9938
9939       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9941     }
9942   }
9943
9944   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9945 }
9946
9947 void CheckExit(int x, int y)
9948 {
9949   if (local_player->gems_still_needed > 0 ||
9950       local_player->sokobanfields_still_needed > 0 ||
9951       local_player->lights_still_needed > 0)
9952   {
9953     int element = Feld[x][y];
9954     int graphic = el2img(element);
9955
9956     if (IS_ANIMATED(graphic))
9957       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958
9959     return;
9960   }
9961
9962   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9963     return;
9964
9965   Feld[x][y] = EL_EXIT_OPENING;
9966
9967   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9968 }
9969
9970 void CheckExitEM(int x, int y)
9971 {
9972   if (local_player->gems_still_needed > 0 ||
9973       local_player->sokobanfields_still_needed > 0 ||
9974       local_player->lights_still_needed > 0)
9975   {
9976     int element = Feld[x][y];
9977     int graphic = el2img(element);
9978
9979     if (IS_ANIMATED(graphic))
9980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9981
9982     return;
9983   }
9984
9985   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9986     return;
9987
9988   Feld[x][y] = EL_EM_EXIT_OPENING;
9989
9990   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9991 }
9992
9993 void CheckExitSteel(int x, int y)
9994 {
9995   if (local_player->gems_still_needed > 0 ||
9996       local_player->sokobanfields_still_needed > 0 ||
9997       local_player->lights_still_needed > 0)
9998   {
9999     int element = Feld[x][y];
10000     int graphic = el2img(element);
10001
10002     if (IS_ANIMATED(graphic))
10003       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10004
10005     return;
10006   }
10007
10008   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10009     return;
10010
10011   Feld[x][y] = EL_STEEL_EXIT_OPENING;
10012
10013   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10014 }
10015
10016 void CheckExitSteelEM(int x, int y)
10017 {
10018   if (local_player->gems_still_needed > 0 ||
10019       local_player->sokobanfields_still_needed > 0 ||
10020       local_player->lights_still_needed > 0)
10021   {
10022     int element = Feld[x][y];
10023     int graphic = el2img(element);
10024
10025     if (IS_ANIMATED(graphic))
10026       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10027
10028     return;
10029   }
10030
10031   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10032     return;
10033
10034   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10035
10036   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10037 }
10038
10039 void CheckExitSP(int x, int y)
10040 {
10041   if (local_player->gems_still_needed > 0)
10042   {
10043     int element = Feld[x][y];
10044     int graphic = el2img(element);
10045
10046     if (IS_ANIMATED(graphic))
10047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10048
10049     return;
10050   }
10051
10052   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10053     return;
10054
10055   Feld[x][y] = EL_SP_EXIT_OPENING;
10056
10057   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10058 }
10059
10060 static void CloseAllOpenTimegates()
10061 {
10062   int x, y;
10063
10064   SCAN_PLAYFIELD(x, y)
10065   {
10066     int element = Feld[x][y];
10067
10068     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10069     {
10070       Feld[x][y] = EL_TIMEGATE_CLOSING;
10071
10072       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10073     }
10074   }
10075 }
10076
10077 void DrawTwinkleOnField(int x, int y)
10078 {
10079   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10080     return;
10081
10082   if (Feld[x][y] == EL_BD_DIAMOND)
10083     return;
10084
10085   if (MovDelay[x][y] == 0)      /* next animation frame */
10086     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10087
10088   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10089   {
10090     MovDelay[x][y]--;
10091
10092     DrawLevelElementAnimation(x, y, Feld[x][y]);
10093
10094     if (MovDelay[x][y] != 0)
10095     {
10096       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097                                            10 - MovDelay[x][y]);
10098
10099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10100     }
10101   }
10102 }
10103
10104 void MauerWaechst(int x, int y)
10105 {
10106   int delay = 6;
10107
10108   if (!MovDelay[x][y])          /* next animation frame */
10109     MovDelay[x][y] = 3 * delay;
10110
10111   if (MovDelay[x][y])           /* wait some time before next frame */
10112   {
10113     MovDelay[x][y]--;
10114
10115     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10116     {
10117       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10119
10120       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10121     }
10122
10123     if (!MovDelay[x][y])
10124     {
10125       if (MovDir[x][y] == MV_LEFT)
10126       {
10127         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128           TEST_DrawLevelField(x - 1, y);
10129       }
10130       else if (MovDir[x][y] == MV_RIGHT)
10131       {
10132         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133           TEST_DrawLevelField(x + 1, y);
10134       }
10135       else if (MovDir[x][y] == MV_UP)
10136       {
10137         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138           TEST_DrawLevelField(x, y - 1);
10139       }
10140       else
10141       {
10142         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143           TEST_DrawLevelField(x, y + 1);
10144       }
10145
10146       Feld[x][y] = Store[x][y];
10147       Store[x][y] = 0;
10148       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149       TEST_DrawLevelField(x, y);
10150     }
10151   }
10152 }
10153
10154 void MauerAbleger(int ax, int ay)
10155 {
10156   int element = Feld[ax][ay];
10157   int graphic = el2img(element);
10158   boolean oben_frei = FALSE, unten_frei = FALSE;
10159   boolean links_frei = FALSE, rechts_frei = FALSE;
10160   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162   boolean new_wall = FALSE;
10163
10164   if (IS_ANIMATED(graphic))
10165     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10166
10167   if (!MovDelay[ax][ay])        /* start building new wall */
10168     MovDelay[ax][ay] = 6;
10169
10170   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10171   {
10172     MovDelay[ax][ay]--;
10173     if (MovDelay[ax][ay])
10174       return;
10175   }
10176
10177   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10178     oben_frei = TRUE;
10179   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10180     unten_frei = TRUE;
10181   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10182     links_frei = TRUE;
10183   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184     rechts_frei = TRUE;
10185
10186   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187       element == EL_EXPANDABLE_WALL_ANY)
10188   {
10189     if (oben_frei)
10190     {
10191       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192       Store[ax][ay-1] = element;
10193       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10197       new_wall = TRUE;
10198     }
10199     if (unten_frei)
10200     {
10201       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202       Store[ax][ay+1] = element;
10203       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10207       new_wall = TRUE;
10208     }
10209   }
10210
10211   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212       element == EL_EXPANDABLE_WALL_ANY ||
10213       element == EL_EXPANDABLE_WALL ||
10214       element == EL_BD_EXPANDABLE_WALL)
10215   {
10216     if (links_frei)
10217     {
10218       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219       Store[ax-1][ay] = element;
10220       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10224       new_wall = TRUE;
10225     }
10226
10227     if (rechts_frei)
10228     {
10229       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230       Store[ax+1][ay] = element;
10231       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10235       new_wall = TRUE;
10236     }
10237   }
10238
10239   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240     TEST_DrawLevelField(ax, ay);
10241
10242   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243     oben_massiv = TRUE;
10244   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245     unten_massiv = TRUE;
10246   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247     links_massiv = TRUE;
10248   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249     rechts_massiv = TRUE;
10250
10251   if (((oben_massiv && unten_massiv) ||
10252        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253        element == EL_EXPANDABLE_WALL) &&
10254       ((links_massiv && rechts_massiv) ||
10255        element == EL_EXPANDABLE_WALL_VERTICAL))
10256     Feld[ax][ay] = EL_WALL;
10257
10258   if (new_wall)
10259     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10260 }
10261
10262 void MauerAblegerStahl(int ax, int ay)
10263 {
10264   int element = Feld[ax][ay];
10265   int graphic = el2img(element);
10266   boolean oben_frei = FALSE, unten_frei = FALSE;
10267   boolean links_frei = FALSE, rechts_frei = FALSE;
10268   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270   boolean new_wall = FALSE;
10271
10272   if (IS_ANIMATED(graphic))
10273     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10274
10275   if (!MovDelay[ax][ay])        /* start building new wall */
10276     MovDelay[ax][ay] = 6;
10277
10278   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10279   {
10280     MovDelay[ax][ay]--;
10281     if (MovDelay[ax][ay])
10282       return;
10283   }
10284
10285   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10286     oben_frei = TRUE;
10287   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10288     unten_frei = TRUE;
10289   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10290     links_frei = TRUE;
10291   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292     rechts_frei = TRUE;
10293
10294   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295       element == EL_EXPANDABLE_STEELWALL_ANY)
10296   {
10297     if (oben_frei)
10298     {
10299       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300       Store[ax][ay-1] = element;
10301       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10305       new_wall = TRUE;
10306     }
10307     if (unten_frei)
10308     {
10309       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310       Store[ax][ay+1] = element;
10311       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10315       new_wall = TRUE;
10316     }
10317   }
10318
10319   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320       element == EL_EXPANDABLE_STEELWALL_ANY)
10321   {
10322     if (links_frei)
10323     {
10324       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325       Store[ax-1][ay] = element;
10326       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10330       new_wall = TRUE;
10331     }
10332
10333     if (rechts_frei)
10334     {
10335       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336       Store[ax+1][ay] = element;
10337       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10341       new_wall = TRUE;
10342     }
10343   }
10344
10345   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346     oben_massiv = TRUE;
10347   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348     unten_massiv = TRUE;
10349   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350     links_massiv = TRUE;
10351   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352     rechts_massiv = TRUE;
10353
10354   if (((oben_massiv && unten_massiv) ||
10355        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356       ((links_massiv && rechts_massiv) ||
10357        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358     Feld[ax][ay] = EL_STEELWALL;
10359
10360   if (new_wall)
10361     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10362 }
10363
10364 void CheckForDragon(int x, int y)
10365 {
10366   int i, j;
10367   boolean dragon_found = FALSE;
10368   static int xy[4][2] =
10369   {
10370     { 0, -1 },
10371     { -1, 0 },
10372     { +1, 0 },
10373     { 0, +1 }
10374   };
10375
10376   for (i = 0; i < NUM_DIRECTIONS; i++)
10377   {
10378     for (j = 0; j < 4; j++)
10379     {
10380       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10381
10382       if (IN_LEV_FIELD(xx, yy) &&
10383           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10384       {
10385         if (Feld[xx][yy] == EL_DRAGON)
10386           dragon_found = TRUE;
10387       }
10388       else
10389         break;
10390     }
10391   }
10392
10393   if (!dragon_found)
10394   {
10395     for (i = 0; i < NUM_DIRECTIONS; i++)
10396     {
10397       for (j = 0; j < 3; j++)
10398       {
10399         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10400   
10401         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10402         {
10403           Feld[xx][yy] = EL_EMPTY;
10404           TEST_DrawLevelField(xx, yy);
10405         }
10406         else
10407           break;
10408       }
10409     }
10410   }
10411 }
10412
10413 static void InitBuggyBase(int x, int y)
10414 {
10415   int element = Feld[x][y];
10416   int activating_delay = FRAMES_PER_SECOND / 4;
10417
10418   ChangeDelay[x][y] =
10419     (element == EL_SP_BUGGY_BASE ?
10420      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10422      activating_delay :
10423      element == EL_SP_BUGGY_BASE_ACTIVE ?
10424      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10425 }
10426
10427 static void WarnBuggyBase(int x, int y)
10428 {
10429   int i;
10430   static int xy[4][2] =
10431   {
10432     { 0, -1 },
10433     { -1, 0 },
10434     { +1, 0 },
10435     { 0, +1 }
10436   };
10437
10438   for (i = 0; i < NUM_DIRECTIONS; i++)
10439   {
10440     int xx = x + xy[i][0];
10441     int yy = y + xy[i][1];
10442
10443     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10444     {
10445       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10446
10447       break;
10448     }
10449   }
10450 }
10451
10452 static void InitTrap(int x, int y)
10453 {
10454   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10455 }
10456
10457 static void ActivateTrap(int x, int y)
10458 {
10459   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10460 }
10461
10462 static void ChangeActiveTrap(int x, int y)
10463 {
10464   int graphic = IMG_TRAP_ACTIVE;
10465
10466   /* if new animation frame was drawn, correct crumbled sand border */
10467   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468     TEST_DrawLevelFieldCrumbled(x, y);
10469 }
10470
10471 static int getSpecialActionElement(int element, int number, int base_element)
10472 {
10473   return (element != EL_EMPTY ? element :
10474           number != -1 ? base_element + number - 1 :
10475           EL_EMPTY);
10476 }
10477
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479                                    int value_min, int value_max)
10480 {
10481   int value_new = (operator == CA_MODE_SET      ? operand :
10482                    operator == CA_MODE_ADD      ? value_old + operand :
10483                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10484                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10485                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10486                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10487                    value_old);
10488
10489   return (value_new < value_min ? value_min :
10490           value_new > value_max ? value_max :
10491           value_new);
10492 }
10493
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10495 {
10496   struct ElementInfo *ei = &element_info[element];
10497   struct ElementChangeInfo *change = &ei->change_page[page];
10498   int target_element = change->target_element;
10499   int action_type = change->action_type;
10500   int action_mode = change->action_mode;
10501   int action_arg = change->action_arg;
10502   int action_element = change->action_element;
10503   int i;
10504
10505   if (!change->has_action)
10506     return;
10507
10508   /* ---------- determine action paramater values -------------------------- */
10509
10510   int level_time_value =
10511     (level.time > 0 ? TimeLeft :
10512      TimePlayed);
10513
10514   int action_arg_element_raw =
10515     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10516      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10518      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10519      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10521      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10522      EL_EMPTY);
10523   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10524
10525 #if 0
10526   if (action_arg_element_raw == EL_GROUP_START)
10527     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10528 #endif
10529
10530   int action_arg_direction =
10531     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534      change->actual_trigger_side :
10535      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10537      MV_NONE);
10538
10539   int action_arg_number_min =
10540     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10541      CA_ARG_MIN);
10542
10543   int action_arg_number_max =
10544     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545      action_type == CA_SET_LEVEL_GEMS ? 999 :
10546      action_type == CA_SET_LEVEL_TIME ? 9999 :
10547      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548      action_type == CA_SET_CE_VALUE ? 9999 :
10549      action_type == CA_SET_CE_SCORE ? 9999 :
10550      CA_ARG_MAX);
10551
10552   int action_arg_number_reset =
10553     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555      action_type == CA_SET_LEVEL_TIME ? level.time :
10556      action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10559 #else
10560      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10561 #endif
10562      action_type == CA_SET_CE_SCORE ? 0 :
10563      0);
10564
10565   int action_arg_number =
10566     (action_arg <= CA_ARG_MAX ? action_arg :
10567      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10575 #else
10576      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10577 #endif
10578      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10590      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10592      -1);
10593
10594   int action_arg_number_old =
10595     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10600      0);
10601
10602   int action_arg_number_new =
10603     getModifiedActionNumber(action_arg_number_old,
10604                             action_mode, action_arg_number,
10605                             action_arg_number_min, action_arg_number_max);
10606
10607 #if 1
10608   int trigger_player_bits =
10609     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610      change->actual_trigger_player_bits : change->trigger_player);
10611 #else
10612   int trigger_player_bits =
10613     (change->actual_trigger_player >= EL_PLAYER_1 &&
10614      change->actual_trigger_player <= EL_PLAYER_4 ?
10615      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10616      PLAYER_BITS_ANY);
10617 #endif
10618
10619   int action_arg_player_bits =
10620     (action_arg >= CA_ARG_PLAYER_1 &&
10621      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10624      PLAYER_BITS_ANY);
10625
10626   /* ---------- execute action  -------------------------------------------- */
10627
10628   switch (action_type)
10629   {
10630     case CA_NO_ACTION:
10631     {
10632       return;
10633     }
10634
10635     /* ---------- level actions  ------------------------------------------- */
10636
10637     case CA_RESTART_LEVEL:
10638     {
10639       game.restart_level = TRUE;
10640
10641       break;
10642     }
10643
10644     case CA_SHOW_ENVELOPE:
10645     {
10646       int element = getSpecialActionElement(action_arg_element,
10647                                             action_arg_number, EL_ENVELOPE_1);
10648
10649       if (IS_ENVELOPE(element))
10650         local_player->show_envelope = element;
10651
10652       break;
10653     }
10654
10655     case CA_SET_LEVEL_TIME:
10656     {
10657       if (level.time > 0)       /* only modify limited time value */
10658       {
10659         TimeLeft = action_arg_number_new;
10660
10661 #if 1
10662         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10663
10664         DisplayGameControlValues();
10665 #else
10666         DrawGameValue_Time(TimeLeft);
10667 #endif
10668
10669         if (!TimeLeft && setup.time_limit)
10670           for (i = 0; i < MAX_PLAYERS; i++)
10671             KillPlayer(&stored_player[i]);
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_LEVEL_SCORE:
10678     {
10679       local_player->score = action_arg_number_new;
10680
10681 #if 1
10682       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10683
10684       DisplayGameControlValues();
10685 #else
10686       DrawGameValue_Score(local_player->score);
10687 #endif
10688
10689       break;
10690     }
10691
10692     case CA_SET_LEVEL_GEMS:
10693     {
10694       local_player->gems_still_needed = action_arg_number_new;
10695
10696 #if 1
10697       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10698
10699       DisplayGameControlValues();
10700 #else
10701       DrawGameValue_Emeralds(local_player->gems_still_needed);
10702 #endif
10703
10704       break;
10705     }
10706
10707 #if !USE_PLAYER_GRAVITY
10708     case CA_SET_LEVEL_GRAVITY:
10709     {
10710       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10711                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10712                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10713                       game.gravity);
10714       break;
10715     }
10716 #endif
10717
10718     case CA_SET_LEVEL_WIND:
10719     {
10720       game.wind_direction = action_arg_direction;
10721
10722       break;
10723     }
10724
10725     case CA_SET_LEVEL_RANDOM_SEED:
10726     {
10727 #if 1
10728       /* ensure that setting a new random seed while playing is predictable */
10729       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10730 #else
10731       InitRND(action_arg_number_new);
10732 #endif
10733
10734 #if 0
10735       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10736 #endif
10737
10738 #if 0
10739       {
10740         int i;
10741
10742         printf("::: ");
10743         for (i = 0; i < 9; i++)
10744           printf("%d, ", RND(2));
10745         printf("\n");
10746       }
10747 #endif
10748
10749       break;
10750     }
10751
10752     /* ---------- player actions  ------------------------------------------ */
10753
10754     case CA_MOVE_PLAYER:
10755     {
10756       /* automatically move to the next field in specified direction */
10757       for (i = 0; i < MAX_PLAYERS; i++)
10758         if (trigger_player_bits & (1 << i))
10759           stored_player[i].programmed_action = action_arg_direction;
10760
10761       break;
10762     }
10763
10764     case CA_EXIT_PLAYER:
10765     {
10766       for (i = 0; i < MAX_PLAYERS; i++)
10767         if (action_arg_player_bits & (1 << i))
10768           PlayerWins(&stored_player[i]);
10769
10770       break;
10771     }
10772
10773     case CA_KILL_PLAYER:
10774     {
10775       for (i = 0; i < MAX_PLAYERS; i++)
10776         if (action_arg_player_bits & (1 << i))
10777           KillPlayer(&stored_player[i]);
10778
10779       break;
10780     }
10781
10782     case CA_SET_PLAYER_KEYS:
10783     {
10784       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785       int element = getSpecialActionElement(action_arg_element,
10786                                             action_arg_number, EL_KEY_1);
10787
10788       if (IS_KEY(element))
10789       {
10790         for (i = 0; i < MAX_PLAYERS; i++)
10791         {
10792           if (trigger_player_bits & (1 << i))
10793           {
10794             stored_player[i].key[KEY_NR(element)] = key_state;
10795
10796             DrawGameDoorValues();
10797           }
10798         }
10799       }
10800
10801       break;
10802     }
10803
10804     case CA_SET_PLAYER_SPEED:
10805     {
10806 #if 0
10807       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10808 #endif
10809
10810       for (i = 0; i < MAX_PLAYERS; i++)
10811       {
10812         if (trigger_player_bits & (1 << i))
10813         {
10814           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10815
10816           if (action_arg == CA_ARG_SPEED_FASTER &&
10817               stored_player[i].cannot_move)
10818           {
10819             action_arg_number = STEPSIZE_VERY_SLOW;
10820           }
10821           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822                    action_arg == CA_ARG_SPEED_FASTER)
10823           {
10824             action_arg_number = 2;
10825             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10826                            CA_MODE_MULTIPLY);
10827           }
10828           else if (action_arg == CA_ARG_NUMBER_RESET)
10829           {
10830             action_arg_number = level.initial_player_stepsize[i];
10831           }
10832
10833           move_stepsize =
10834             getModifiedActionNumber(move_stepsize,
10835                                     action_mode,
10836                                     action_arg_number,
10837                                     action_arg_number_min,
10838                                     action_arg_number_max);
10839
10840           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10841         }
10842       }
10843
10844       break;
10845     }
10846
10847     case CA_SET_PLAYER_SHIELD:
10848     {
10849       for (i = 0; i < MAX_PLAYERS; i++)
10850       {
10851         if (trigger_player_bits & (1 << i))
10852         {
10853           if (action_arg == CA_ARG_SHIELD_OFF)
10854           {
10855             stored_player[i].shield_normal_time_left = 0;
10856             stored_player[i].shield_deadly_time_left = 0;
10857           }
10858           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10859           {
10860             stored_player[i].shield_normal_time_left = 999999;
10861           }
10862           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10863           {
10864             stored_player[i].shield_normal_time_left = 999999;
10865             stored_player[i].shield_deadly_time_left = 999999;
10866           }
10867         }
10868       }
10869
10870       break;
10871     }
10872
10873 #if USE_PLAYER_GRAVITY
10874     case CA_SET_PLAYER_GRAVITY:
10875     {
10876       for (i = 0; i < MAX_PLAYERS; i++)
10877       {
10878         if (trigger_player_bits & (1 << i))
10879         {
10880           stored_player[i].gravity =
10881             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10882              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10883              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884              stored_player[i].gravity);
10885         }
10886       }
10887
10888       break;
10889     }
10890 #endif
10891
10892     case CA_SET_PLAYER_ARTWORK:
10893     {
10894       for (i = 0; i < MAX_PLAYERS; i++)
10895       {
10896         if (trigger_player_bits & (1 << i))
10897         {
10898           int artwork_element = action_arg_element;
10899
10900           if (action_arg == CA_ARG_ELEMENT_RESET)
10901             artwork_element =
10902               (level.use_artwork_element[i] ? level.artwork_element[i] :
10903                stored_player[i].element_nr);
10904
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906           if (stored_player[i].artwork_element != artwork_element)
10907             stored_player[i].Frame = 0;
10908 #endif
10909
10910           stored_player[i].artwork_element = artwork_element;
10911
10912           SetPlayerWaiting(&stored_player[i], FALSE);
10913
10914           /* set number of special actions for bored and sleeping animation */
10915           stored_player[i].num_special_action_bored =
10916             get_num_special_action(artwork_element,
10917                                    ACTION_BORING_1, ACTION_BORING_LAST);
10918           stored_player[i].num_special_action_sleeping =
10919             get_num_special_action(artwork_element,
10920                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10921         }
10922       }
10923
10924       break;
10925     }
10926
10927     case CA_SET_PLAYER_INVENTORY:
10928     {
10929       for (i = 0; i < MAX_PLAYERS; i++)
10930       {
10931         struct PlayerInfo *player = &stored_player[i];
10932         int j, k;
10933
10934         if (trigger_player_bits & (1 << i))
10935         {
10936           int inventory_element = action_arg_element;
10937
10938           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940               action_arg == CA_ARG_ELEMENT_ACTION)
10941           {
10942             int element = inventory_element;
10943             int collect_count = element_info[element].collect_count_initial;
10944
10945             if (!IS_CUSTOM_ELEMENT(element))
10946               collect_count = 1;
10947
10948             if (collect_count == 0)
10949               player->inventory_infinite_element = element;
10950             else
10951               for (k = 0; k < collect_count; k++)
10952                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953                   player->inventory_element[player->inventory_size++] =
10954                     element;
10955           }
10956           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10959           {
10960             if (player->inventory_infinite_element != EL_UNDEFINED &&
10961                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962                                      action_arg_element_raw))
10963               player->inventory_infinite_element = EL_UNDEFINED;
10964
10965             for (k = 0, j = 0; j < player->inventory_size; j++)
10966             {
10967               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968                                         action_arg_element_raw))
10969                 player->inventory_element[k++] = player->inventory_element[j];
10970             }
10971
10972             player->inventory_size = k;
10973           }
10974           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10975           {
10976             if (player->inventory_size > 0)
10977             {
10978               for (j = 0; j < player->inventory_size - 1; j++)
10979                 player->inventory_element[j] = player->inventory_element[j + 1];
10980
10981               player->inventory_size--;
10982             }
10983           }
10984           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10985           {
10986             if (player->inventory_size > 0)
10987               player->inventory_size--;
10988           }
10989           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10990           {
10991             player->inventory_infinite_element = EL_UNDEFINED;
10992             player->inventory_size = 0;
10993           }
10994           else if (action_arg == CA_ARG_INVENTORY_RESET)
10995           {
10996             player->inventory_infinite_element = EL_UNDEFINED;
10997             player->inventory_size = 0;
10998
10999             if (level.use_initial_inventory[i])
11000             {
11001               for (j = 0; j < level.initial_inventory_size[i]; j++)
11002               {
11003                 int element = level.initial_inventory_content[i][j];
11004                 int collect_count = element_info[element].collect_count_initial;
11005
11006                 if (!IS_CUSTOM_ELEMENT(element))
11007                   collect_count = 1;
11008
11009                 if (collect_count == 0)
11010                   player->inventory_infinite_element = element;
11011                 else
11012                   for (k = 0; k < collect_count; k++)
11013                     if (player->inventory_size < MAX_INVENTORY_SIZE)
11014                       player->inventory_element[player->inventory_size++] =
11015                         element;
11016               }
11017             }
11018           }
11019         }
11020       }
11021
11022       break;
11023     }
11024
11025     /* ---------- CE actions  ---------------------------------------------- */
11026
11027     case CA_SET_CE_VALUE:
11028     {
11029 #if USE_NEW_CUSTOM_VALUE
11030       int last_ce_value = CustomValue[x][y];
11031
11032       CustomValue[x][y] = action_arg_number_new;
11033
11034       if (CustomValue[x][y] != last_ce_value)
11035       {
11036         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11038
11039         if (CustomValue[x][y] == 0)
11040         {
11041           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11043         }
11044       }
11045 #endif
11046
11047       break;
11048     }
11049
11050     case CA_SET_CE_SCORE:
11051     {
11052 #if USE_NEW_CUSTOM_VALUE
11053       int last_ce_score = ei->collect_score;
11054
11055       ei->collect_score = action_arg_number_new;
11056
11057       if (ei->collect_score != last_ce_score)
11058       {
11059         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11061
11062         if (ei->collect_score == 0)
11063         {
11064           int xx, yy;
11065
11066           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11068
11069           /*
11070             This is a very special case that seems to be a mixture between
11071             CheckElementChange() and CheckTriggeredElementChange(): while
11072             the first one only affects single elements that are triggered
11073             directly, the second one affects multiple elements in the playfield
11074             that are triggered indirectly by another element. This is a third
11075             case: Changing the CE score always affects multiple identical CEs,
11076             so every affected CE must be checked, not only the single CE for
11077             which the CE score was changed in the first place (as every instance
11078             of that CE shares the same CE score, and therefore also can change)!
11079           */
11080           SCAN_PLAYFIELD(xx, yy)
11081           {
11082             if (Feld[xx][yy] == element)
11083               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084                                  CE_SCORE_GETS_ZERO);
11085           }
11086         }
11087       }
11088 #endif
11089
11090       break;
11091     }
11092
11093     case CA_SET_CE_ARTWORK:
11094     {
11095       int artwork_element = action_arg_element;
11096       boolean reset_frame = FALSE;
11097       int xx, yy;
11098
11099       if (action_arg == CA_ARG_ELEMENT_RESET)
11100         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11101                            element);
11102
11103       if (ei->gfx_element != artwork_element)
11104         reset_frame = TRUE;
11105
11106       ei->gfx_element = artwork_element;
11107
11108       SCAN_PLAYFIELD(xx, yy)
11109       {
11110         if (Feld[xx][yy] == element)
11111         {
11112           if (reset_frame)
11113           {
11114             ResetGfxAnimation(xx, yy);
11115             ResetRandomAnimationValue(xx, yy);
11116           }
11117
11118           TEST_DrawLevelField(xx, yy);
11119         }
11120       }
11121
11122       break;
11123     }
11124
11125     /* ---------- engine actions  ------------------------------------------ */
11126
11127     case CA_SET_ENGINE_SCAN_MODE:
11128     {
11129       InitPlayfieldScanMode(action_arg);
11130
11131       break;
11132     }
11133
11134     default:
11135       break;
11136   }
11137 }
11138
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11140 {
11141   int old_element = Feld[x][y];
11142   int new_element = GetElementFromGroupElement(element);
11143   int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145   int last_ce_value = CustomValue[x][y];
11146 #endif
11147   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149   boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151                                      /* this breaks SnakeBite when a snake is
11152                                         halfway through a door that closes */
11153                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11154                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11155 #endif
11156                                      IS_WALKABLE(old_element));
11157
11158 #if 0
11159   /* check if element under the player changes from accessible to unaccessible
11160      (needed for special case of dropping element which then changes) */
11161   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11163   {
11164     Bang(x, y);
11165
11166     return;
11167   }
11168 #endif
11169
11170   if (!add_player_onto_element)
11171   {
11172     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173       RemoveMovingField(x, y);
11174     else
11175       RemoveField(x, y);
11176
11177     Feld[x][y] = new_element;
11178
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180     ResetGfxAnimation(x, y);
11181     ResetRandomAnimationValue(x, y);
11182 #endif
11183
11184     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185       MovDir[x][y] = previous_move_direction;
11186
11187 #if USE_NEW_CUSTOM_VALUE
11188     if (element_info[new_element].use_last_ce_value)
11189       CustomValue[x][y] = last_ce_value;
11190 #endif
11191
11192     InitField_WithBug1(x, y, FALSE);
11193
11194     new_element = Feld[x][y];   /* element may have changed */
11195
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197     ResetGfxAnimation(x, y);
11198     ResetRandomAnimationValue(x, y);
11199 #endif
11200
11201     TEST_DrawLevelField(x, y);
11202
11203     if (GFX_CRUMBLED(new_element))
11204       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11205   }
11206
11207 #if 1
11208   /* check if element under the player changes from accessible to unaccessible
11209      (needed for special case of dropping element which then changes) */
11210   /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11214   {
11215     Bang(x, y);
11216
11217     return;
11218   }
11219 #else
11220   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11222   {
11223     Bang(x, y);
11224
11225     return;
11226   }
11227 #endif
11228 #endif
11229
11230   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231   if (new_element_is_player)
11232     RelocatePlayer(x, y, new_element);
11233
11234   if (is_change)
11235     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11236
11237   TestIfBadThingTouchesPlayer(x, y);
11238   TestIfPlayerTouchesCustomElement(x, y);
11239   TestIfElementTouchesCustomElement(x, y);
11240 }
11241
11242 static void CreateField(int x, int y, int element)
11243 {
11244   CreateFieldExt(x, y, element, FALSE);
11245 }
11246
11247 static void CreateElementFromChange(int x, int y, int element)
11248 {
11249   element = GET_VALID_RUNTIME_ELEMENT(element);
11250
11251 #if USE_STOP_CHANGED_ELEMENTS
11252   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11253   {
11254     int old_element = Feld[x][y];
11255
11256     /* prevent changed element from moving in same engine frame
11257        unless both old and new element can either fall or move */
11258     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11260       Stop[x][y] = TRUE;
11261   }
11262 #endif
11263
11264   CreateFieldExt(x, y, element, TRUE);
11265 }
11266
11267 static boolean ChangeElement(int x, int y, int element, int page)
11268 {
11269   struct ElementInfo *ei = &element_info[element];
11270   struct ElementChangeInfo *change = &ei->change_page[page];
11271   int ce_value = CustomValue[x][y];
11272   int ce_score = ei->collect_score;
11273   int target_element;
11274   int old_element = Feld[x][y];
11275
11276   /* always use default change event to prevent running into a loop */
11277   if (ChangeEvent[x][y] == -1)
11278     ChangeEvent[x][y] = CE_DELAY;
11279
11280   if (ChangeEvent[x][y] == CE_DELAY)
11281   {
11282     /* reset actual trigger element, trigger player and action element */
11283     change->actual_trigger_element = EL_EMPTY;
11284     change->actual_trigger_player = EL_EMPTY;
11285     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286     change->actual_trigger_side = CH_SIDE_NONE;
11287     change->actual_trigger_ce_value = 0;
11288     change->actual_trigger_ce_score = 0;
11289   }
11290
11291   /* do not change elements more than a specified maximum number of changes */
11292   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11293     return FALSE;
11294
11295   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11296
11297   if (change->explode)
11298   {
11299     Bang(x, y);
11300
11301     return TRUE;
11302   }
11303
11304   if (change->use_target_content)
11305   {
11306     boolean complete_replace = TRUE;
11307     boolean can_replace[3][3];
11308     int xx, yy;
11309
11310     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11311     {
11312       boolean is_empty;
11313       boolean is_walkable;
11314       boolean is_diggable;
11315       boolean is_collectible;
11316       boolean is_removable;
11317       boolean is_destructible;
11318       int ex = x + xx - 1;
11319       int ey = y + yy - 1;
11320       int content_element = change->target_content.e[xx][yy];
11321       int e;
11322
11323       can_replace[xx][yy] = TRUE;
11324
11325       if (ex == x && ey == y)   /* do not check changing element itself */
11326         continue;
11327
11328       if (content_element == EL_EMPTY_SPACE)
11329       {
11330         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11331
11332         continue;
11333       }
11334
11335       if (!IN_LEV_FIELD(ex, ey))
11336       {
11337         can_replace[xx][yy] = FALSE;
11338         complete_replace = FALSE;
11339
11340         continue;
11341       }
11342
11343       e = Feld[ex][ey];
11344
11345       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346         e = MovingOrBlocked2Element(ex, ey);
11347
11348       is_empty = (IS_FREE(ex, ey) ||
11349                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11350
11351       is_walkable     = (is_empty || IS_WALKABLE(e));
11352       is_diggable     = (is_empty || IS_DIGGABLE(e));
11353       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11354       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355       is_removable    = (is_diggable || is_collectible);
11356
11357       can_replace[xx][yy] =
11358         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11359           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11360           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11361           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11362           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11363           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11365
11366       if (!can_replace[xx][yy])
11367         complete_replace = FALSE;
11368     }
11369
11370     if (!change->only_if_complete || complete_replace)
11371     {
11372       boolean something_has_changed = FALSE;
11373
11374       if (change->only_if_complete && change->use_random_replace &&
11375           RND(100) < change->random_percentage)
11376         return FALSE;
11377
11378       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11379       {
11380         int ex = x + xx - 1;
11381         int ey = y + yy - 1;
11382         int content_element;
11383
11384         if (can_replace[xx][yy] && (!change->use_random_replace ||
11385                                     RND(100) < change->random_percentage))
11386         {
11387           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388             RemoveMovingField(ex, ey);
11389
11390           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11391
11392           content_element = change->target_content.e[xx][yy];
11393           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394                                               ce_value, ce_score);
11395
11396           CreateElementFromChange(ex, ey, target_element);
11397
11398           something_has_changed = TRUE;
11399
11400           /* for symmetry reasons, freeze newly created border elements */
11401           if (ex != x || ey != y)
11402             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11403         }
11404       }
11405
11406       if (something_has_changed)
11407       {
11408         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11410       }
11411     }
11412   }
11413   else
11414   {
11415     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416                                         ce_value, ce_score);
11417
11418     if (element == EL_DIAGONAL_GROWING ||
11419         element == EL_DIAGONAL_SHRINKING)
11420     {
11421       target_element = Store[x][y];
11422
11423       Store[x][y] = EL_EMPTY;
11424     }
11425
11426     CreateElementFromChange(x, y, target_element);
11427
11428     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11430   }
11431
11432   /* this uses direct change before indirect change */
11433   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11434
11435   return TRUE;
11436 }
11437
11438 #if USE_NEW_DELAYED_ACTION
11439
11440 static void HandleElementChange(int x, int y, int page)
11441 {
11442   int element = MovingOrBlocked2Element(x, y);
11443   struct ElementInfo *ei = &element_info[element];
11444   struct ElementChangeInfo *change = &ei->change_page[page];
11445   boolean handle_action_before_change = FALSE;
11446
11447 #ifdef DEBUG
11448   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11450   {
11451     printf("\n\n");
11452     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453            x, y, element, element_info[element].token_name);
11454     printf("HandleElementChange(): This should never happen!\n");
11455     printf("\n\n");
11456   }
11457 #endif
11458
11459   /* this can happen with classic bombs on walkable, changing elements */
11460   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11461   {
11462 #if 0
11463     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11464       ChangeDelay[x][y] = 0;
11465 #endif
11466
11467     return;
11468   }
11469
11470   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11471   {
11472     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11473
11474     if (change->can_change)
11475     {
11476 #if 1
11477       /* !!! not clear why graphic animation should be reset at all here !!! */
11478       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480       /* when a custom element is about to change (for example by change delay),
11481          do not reset graphic animation when the custom element is moving */
11482       if (!IS_MOVING(x, y))
11483 #endif
11484       {
11485         ResetGfxAnimation(x, y);
11486         ResetRandomAnimationValue(x, y);
11487       }
11488 #endif
11489
11490       if (change->pre_change_function)
11491         change->pre_change_function(x, y);
11492     }
11493   }
11494
11495   ChangeDelay[x][y]--;
11496
11497   if (ChangeDelay[x][y] != 0)           /* continue element change */
11498   {
11499     if (change->can_change)
11500     {
11501       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11502
11503       if (IS_ANIMATED(graphic))
11504         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505
11506       if (change->change_function)
11507         change->change_function(x, y);
11508     }
11509   }
11510   else                                  /* finish element change */
11511   {
11512     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11513     {
11514       page = ChangePage[x][y];
11515       ChangePage[x][y] = -1;
11516
11517       change = &ei->change_page[page];
11518     }
11519
11520     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11521     {
11522       ChangeDelay[x][y] = 1;            /* try change after next move step */
11523       ChangePage[x][y] = page;          /* remember page to use for change */
11524
11525       return;
11526     }
11527
11528 #if 1
11529     /* special case: set new level random seed before changing element */
11530     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531       handle_action_before_change = TRUE;
11532
11533     if (change->has_action && handle_action_before_change)
11534       ExecuteCustomElementAction(x, y, element, page);
11535 #endif
11536
11537     if (change->can_change)
11538     {
11539       if (ChangeElement(x, y, element, page))
11540       {
11541         if (change->post_change_function)
11542           change->post_change_function(x, y);
11543       }
11544     }
11545
11546     if (change->has_action && !handle_action_before_change)
11547       ExecuteCustomElementAction(x, y, element, page);
11548   }
11549 }
11550
11551 #else
11552
11553 static void HandleElementChange(int x, int y, int page)
11554 {
11555   int element = MovingOrBlocked2Element(x, y);
11556   struct ElementInfo *ei = &element_info[element];
11557   struct ElementChangeInfo *change = &ei->change_page[page];
11558
11559 #ifdef DEBUG
11560   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11561   {
11562     printf("\n\n");
11563     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564            x, y, element, element_info[element].token_name);
11565     printf("HandleElementChange(): This should never happen!\n");
11566     printf("\n\n");
11567   }
11568 #endif
11569
11570   /* this can happen with classic bombs on walkable, changing elements */
11571   if (!CAN_CHANGE(element))
11572   {
11573 #if 0
11574     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11575       ChangeDelay[x][y] = 0;
11576 #endif
11577
11578     return;
11579   }
11580
11581   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11582   {
11583     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11584
11585     ResetGfxAnimation(x, y);
11586     ResetRandomAnimationValue(x, y);
11587
11588     if (change->pre_change_function)
11589       change->pre_change_function(x, y);
11590   }
11591
11592   ChangeDelay[x][y]--;
11593
11594   if (ChangeDelay[x][y] != 0)           /* continue element change */
11595   {
11596     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11597
11598     if (IS_ANIMATED(graphic))
11599       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11600
11601     if (change->change_function)
11602       change->change_function(x, y);
11603   }
11604   else                                  /* finish element change */
11605   {
11606     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11607     {
11608       page = ChangePage[x][y];
11609       ChangePage[x][y] = -1;
11610
11611       change = &ei->change_page[page];
11612     }
11613
11614     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11615     {
11616       ChangeDelay[x][y] = 1;            /* try change after next move step */
11617       ChangePage[x][y] = page;          /* remember page to use for change */
11618
11619       return;
11620     }
11621
11622     if (ChangeElement(x, y, element, page))
11623     {
11624       if (change->post_change_function)
11625         change->post_change_function(x, y);
11626     }
11627   }
11628 }
11629
11630 #endif
11631
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633                                               int trigger_element,
11634                                               int trigger_event,
11635                                               int trigger_player,
11636                                               int trigger_side,
11637                                               int trigger_page)
11638 {
11639   boolean change_done_any = FALSE;
11640   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11641   int i;
11642
11643   if (!(trigger_events[trigger_element][trigger_event]))
11644     return FALSE;
11645
11646 #if 0
11647   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648          trigger_event, recursion_loop_depth, recursion_loop_detected,
11649          recursion_loop_element, EL_NAME(recursion_loop_element));
11650 #endif
11651
11652   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11653
11654   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11655   {
11656     int element = EL_CUSTOM_START + i;
11657     boolean change_done = FALSE;
11658     int p;
11659
11660     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11662       continue;
11663
11664     for (p = 0; p < element_info[element].num_change_pages; p++)
11665     {
11666       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11667
11668       if (change->can_change_or_has_action &&
11669           change->has_event[trigger_event] &&
11670           change->trigger_side & trigger_side &&
11671           change->trigger_player & trigger_player &&
11672           change->trigger_page & trigger_page_bits &&
11673           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11674       {
11675         change->actual_trigger_element = trigger_element;
11676         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677         change->actual_trigger_player_bits = trigger_player;
11678         change->actual_trigger_side = trigger_side;
11679         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11681
11682 #if 0
11683         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684                element, EL_NAME(element), p);
11685 #endif
11686
11687         if ((change->can_change && !change_done) || change->has_action)
11688         {
11689           int x, y;
11690
11691           SCAN_PLAYFIELD(x, y)
11692           {
11693             if (Feld[x][y] == element)
11694             {
11695               if (change->can_change && !change_done)
11696               {
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698                 /* if element already changed in this frame, not only prevent
11699                    another element change (checked in ChangeElement()), but
11700                    also prevent additional element actions for this element */
11701
11702                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703                     !level.use_action_after_change_bug)
11704                   continue;
11705 #endif
11706
11707 #if 0
11708                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709                        element, EL_NAME(element), p);
11710 #endif
11711
11712                 ChangeDelay[x][y] = 1;
11713                 ChangeEvent[x][y] = trigger_event;
11714
11715                 HandleElementChange(x, y, p);
11716               }
11717 #if USE_NEW_DELAYED_ACTION
11718               else if (change->has_action)
11719               {
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721                 /* if element already changed in this frame, not only prevent
11722                    another element change (checked in ChangeElement()), but
11723                    also prevent additional element actions for this element */
11724
11725                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726                     !level.use_action_after_change_bug)
11727                   continue;
11728 #endif
11729
11730
11731 #if 0
11732                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733                        element, EL_NAME(element), p);
11734 #endif
11735
11736                 ExecuteCustomElementAction(x, y, element, p);
11737                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11738               }
11739 #else
11740               if (change->has_action)
11741               {
11742                 ExecuteCustomElementAction(x, y, element, p);
11743                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11744               }
11745 #endif
11746             }
11747           }
11748
11749           if (change->can_change)
11750           {
11751             change_done = TRUE;
11752             change_done_any = TRUE;
11753
11754 #if 0
11755             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756                    element, EL_NAME(element), p);
11757 #endif
11758
11759           }
11760         }
11761       }
11762     }
11763   }
11764
11765   RECURSION_LOOP_DETECTION_END();
11766
11767   return change_done_any;
11768 }
11769
11770 static boolean CheckElementChangeExt(int x, int y,
11771                                      int element,
11772                                      int trigger_element,
11773                                      int trigger_event,
11774                                      int trigger_player,
11775                                      int trigger_side)
11776 {
11777   boolean change_done = FALSE;
11778   int p;
11779
11780   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11782     return FALSE;
11783
11784   if (Feld[x][y] == EL_BLOCKED)
11785   {
11786     Blocked2Moving(x, y, &x, &y);
11787     element = Feld[x][y];
11788   }
11789
11790 #if 0
11791   /* check if element has already changed */
11792   if (Feld[x][y] != element)
11793     return FALSE;
11794 #else
11795   /* check if element has already changed or is about to change after moving */
11796   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797        Feld[x][y] != element) ||
11798
11799       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801         ChangePage[x][y] != -1)))
11802     return FALSE;
11803 #endif
11804
11805 #if 0
11806   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807          trigger_event, recursion_loop_depth, recursion_loop_detected,
11808          recursion_loop_element, EL_NAME(recursion_loop_element));
11809 #endif
11810
11811   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11812
11813 #if 0
11814   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11815 #endif
11816
11817   for (p = 0; p < element_info[element].num_change_pages; p++)
11818   {
11819     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11820
11821     /* check trigger element for all events where the element that is checked
11822        for changing interacts with a directly adjacent element -- this is
11823        different to element changes that affect other elements to change on the
11824        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825     boolean check_trigger_element =
11826       (trigger_event == CE_TOUCHING_X ||
11827        trigger_event == CE_HITTING_X ||
11828        trigger_event == CE_HIT_BY_X ||
11829 #if 1
11830        /* this one was forgotten until 3.2.3 */
11831        trigger_event == CE_DIGGING_X);
11832 #endif
11833
11834     if (change->can_change_or_has_action &&
11835         change->has_event[trigger_event] &&
11836         change->trigger_side & trigger_side &&
11837         change->trigger_player & trigger_player &&
11838         (!check_trigger_element ||
11839          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11840     {
11841       change->actual_trigger_element = trigger_element;
11842       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843       change->actual_trigger_player_bits = trigger_player;
11844       change->actual_trigger_side = trigger_side;
11845       change->actual_trigger_ce_value = CustomValue[x][y];
11846       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11847
11848       /* special case: trigger element not at (x,y) position for some events */
11849       if (check_trigger_element)
11850       {
11851         static struct
11852         {
11853           int dx, dy;
11854         } move_xy[] =
11855           {
11856             {  0,  0 },
11857             { -1,  0 },
11858             { +1,  0 },
11859             {  0,  0 },
11860             {  0, -1 },
11861             {  0,  0 }, { 0, 0 }, { 0, 0 },
11862             {  0, +1 }
11863           };
11864
11865         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11867
11868         change->actual_trigger_ce_value = CustomValue[xx][yy];
11869         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11870       }
11871
11872       if (change->can_change && !change_done)
11873       {
11874         ChangeDelay[x][y] = 1;
11875         ChangeEvent[x][y] = trigger_event;
11876
11877         HandleElementChange(x, y, p);
11878
11879         change_done = TRUE;
11880       }
11881 #if USE_NEW_DELAYED_ACTION
11882       else if (change->has_action)
11883       {
11884         ExecuteCustomElementAction(x, y, element, p);
11885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11886       }
11887 #else
11888       if (change->has_action)
11889       {
11890         ExecuteCustomElementAction(x, y, element, p);
11891         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11892       }
11893 #endif
11894     }
11895   }
11896
11897   RECURSION_LOOP_DETECTION_END();
11898
11899   return change_done;
11900 }
11901
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11903 {
11904   int jx = player->jx, jy = player->jy;
11905   int sound_element = player->artwork_element;
11906   int last_action = player->last_action_waiting;
11907   int action = player->action_waiting;
11908
11909   if (player->is_waiting)
11910   {
11911     if (action != last_action)
11912       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11913     else
11914       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11915   }
11916   else
11917   {
11918     if (action != last_action)
11919       StopSound(element_info[sound_element].sound[last_action]);
11920
11921     if (last_action == ACTION_SLEEPING)
11922       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11923   }
11924 }
11925
11926 static void PlayAllPlayersSound()
11927 {
11928   int i;
11929
11930   for (i = 0; i < MAX_PLAYERS; i++)
11931     if (stored_player[i].active)
11932       PlayPlayerSound(&stored_player[i]);
11933 }
11934
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11936 {
11937   boolean last_waiting = player->is_waiting;
11938   int move_dir = player->MovDir;
11939
11940   player->dir_waiting = move_dir;
11941   player->last_action_waiting = player->action_waiting;
11942
11943   if (is_waiting)
11944   {
11945     if (!last_waiting)          /* not waiting -> waiting */
11946     {
11947       player->is_waiting = TRUE;
11948
11949       player->frame_counter_bored =
11950         FrameCounter +
11951         game.player_boring_delay_fixed +
11952         GetSimpleRandom(game.player_boring_delay_random);
11953       player->frame_counter_sleeping =
11954         FrameCounter +
11955         game.player_sleeping_delay_fixed +
11956         GetSimpleRandom(game.player_sleeping_delay_random);
11957
11958       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11959     }
11960
11961     if (game.player_sleeping_delay_fixed +
11962         game.player_sleeping_delay_random > 0 &&
11963         player->anim_delay_counter == 0 &&
11964         player->post_delay_counter == 0 &&
11965         FrameCounter >= player->frame_counter_sleeping)
11966       player->is_sleeping = TRUE;
11967     else if (game.player_boring_delay_fixed +
11968              game.player_boring_delay_random > 0 &&
11969              FrameCounter >= player->frame_counter_bored)
11970       player->is_bored = TRUE;
11971
11972     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973                               player->is_bored ? ACTION_BORING :
11974                               ACTION_WAITING);
11975
11976     if (player->is_sleeping && player->use_murphy)
11977     {
11978       /* special case for sleeping Murphy when leaning against non-free tile */
11979
11980       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982            !IS_MOVING(player->jx - 1, player->jy)))
11983         move_dir = MV_LEFT;
11984       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986                 !IS_MOVING(player->jx + 1, player->jy)))
11987         move_dir = MV_RIGHT;
11988       else
11989         player->is_sleeping = FALSE;
11990
11991       player->dir_waiting = move_dir;
11992     }
11993
11994     if (player->is_sleeping)
11995     {
11996       if (player->num_special_action_sleeping > 0)
11997       {
11998         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11999         {
12000           int last_special_action = player->special_action_sleeping;
12001           int num_special_action = player->num_special_action_sleeping;
12002           int special_action =
12003             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006              last_special_action + 1 : ACTION_SLEEPING);
12007           int special_graphic =
12008             el_act_dir2img(player->artwork_element, special_action, move_dir);
12009
12010           player->anim_delay_counter =
12011             graphic_info[special_graphic].anim_delay_fixed +
12012             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013           player->post_delay_counter =
12014             graphic_info[special_graphic].post_delay_fixed +
12015             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12016
12017           player->special_action_sleeping = special_action;
12018         }
12019
12020         if (player->anim_delay_counter > 0)
12021         {
12022           player->action_waiting = player->special_action_sleeping;
12023           player->anim_delay_counter--;
12024         }
12025         else if (player->post_delay_counter > 0)
12026         {
12027           player->post_delay_counter--;
12028         }
12029       }
12030     }
12031     else if (player->is_bored)
12032     {
12033       if (player->num_special_action_bored > 0)
12034       {
12035         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12036         {
12037           int special_action =
12038             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039           int special_graphic =
12040             el_act_dir2img(player->artwork_element, special_action, move_dir);
12041
12042           player->anim_delay_counter =
12043             graphic_info[special_graphic].anim_delay_fixed +
12044             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045           player->post_delay_counter =
12046             graphic_info[special_graphic].post_delay_fixed +
12047             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12048
12049           player->special_action_bored = special_action;
12050         }
12051
12052         if (player->anim_delay_counter > 0)
12053         {
12054           player->action_waiting = player->special_action_bored;
12055           player->anim_delay_counter--;
12056         }
12057         else if (player->post_delay_counter > 0)
12058         {
12059           player->post_delay_counter--;
12060         }
12061       }
12062     }
12063   }
12064   else if (last_waiting)        /* waiting -> not waiting */
12065   {
12066     player->is_waiting = FALSE;
12067     player->is_bored = FALSE;
12068     player->is_sleeping = FALSE;
12069
12070     player->frame_counter_bored = -1;
12071     player->frame_counter_sleeping = -1;
12072
12073     player->anim_delay_counter = 0;
12074     player->post_delay_counter = 0;
12075
12076     player->dir_waiting = player->MovDir;
12077     player->action_waiting = ACTION_DEFAULT;
12078
12079     player->special_action_bored = ACTION_DEFAULT;
12080     player->special_action_sleeping = ACTION_DEFAULT;
12081   }
12082 }
12083
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12085 {
12086   if (tape.single_step && tape.recording && !tape.pausing)
12087   {
12088     /* as it is called "single step mode", just return to pause mode when the
12089        player stopped moving after one tile (or never starts moving at all) */
12090     if (!player->is_moving && !player->is_pushing)
12091     {
12092       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093       SnapField(player, 0, 0);                  /* stop snapping */
12094     }
12095   }
12096 }
12097
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12099 {
12100   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12101   int left      = player_action & JOY_LEFT;
12102   int right     = player_action & JOY_RIGHT;
12103   int up        = player_action & JOY_UP;
12104   int down      = player_action & JOY_DOWN;
12105   int button1   = player_action & JOY_BUTTON_1;
12106   int button2   = player_action & JOY_BUTTON_2;
12107   int dx        = (left ? -1 : right ? 1 : 0);
12108   int dy        = (up   ? -1 : down  ? 1 : 0);
12109
12110   if (!player->active || tape.pausing)
12111     return 0;
12112
12113   if (player_action)
12114   {
12115     if (button1)
12116       snapped = SnapField(player, dx, dy);
12117     else
12118     {
12119       if (button2)
12120         dropped = DropElement(player);
12121
12122       moved = MovePlayer(player, dx, dy);
12123     }
12124
12125     CheckSingleStepMode(player);
12126
12127     SetPlayerWaiting(player, FALSE);
12128
12129     return player_action;
12130   }
12131   else
12132   {
12133     /* no actions for this player (no input at player's configured device) */
12134
12135     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12136     SnapField(player, 0, 0);
12137     CheckGravityMovementWhenNotMoving(player);
12138
12139     if (player->MovPos == 0)
12140       SetPlayerWaiting(player, TRUE);
12141
12142     if (player->MovPos == 0)    /* needed for tape.playing */
12143       player->is_moving = FALSE;
12144
12145     player->is_dropping = FALSE;
12146     player->is_dropping_pressed = FALSE;
12147     player->drop_pressed_delay = 0;
12148
12149     CheckSingleStepMode(player);
12150
12151     return 0;
12152   }
12153 }
12154
12155 static void CheckLevelTime()
12156 {
12157   int i;
12158
12159   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12160   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12161   {
12162     if (level.native_em_level->lev->home == 0)  /* all players at home */
12163     {
12164       PlayerWins(local_player);
12165
12166       AllPlayersGone = TRUE;
12167
12168       level.native_em_level->lev->home = -1;
12169     }
12170
12171     if (level.native_em_level->ply[0]->alive == 0 &&
12172         level.native_em_level->ply[1]->alive == 0 &&
12173         level.native_em_level->ply[2]->alive == 0 &&
12174         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12175       AllPlayersGone = TRUE;
12176   }
12177   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12178   {
12179     if (game_sp.LevelSolved &&
12180         !game_sp.GameOver)                              /* game won */
12181     {
12182       PlayerWins(local_player);
12183
12184       game_sp.GameOver = TRUE;
12185
12186       AllPlayersGone = TRUE;
12187     }
12188
12189     if (game_sp.GameOver)                               /* game lost */
12190       AllPlayersGone = TRUE;
12191   }
12192
12193   if (TimeFrames >= FRAMES_PER_SECOND)
12194   {
12195     TimeFrames = 0;
12196     TapeTime++;
12197
12198     for (i = 0; i < MAX_PLAYERS; i++)
12199     {
12200       struct PlayerInfo *player = &stored_player[i];
12201
12202       if (SHIELD_ON(player))
12203       {
12204         player->shield_normal_time_left--;
12205
12206         if (player->shield_deadly_time_left > 0)
12207           player->shield_deadly_time_left--;
12208       }
12209     }
12210
12211     if (!local_player->LevelSolved && !level.use_step_counter)
12212     {
12213       TimePlayed++;
12214
12215       if (TimeLeft > 0)
12216       {
12217         TimeLeft--;
12218
12219         if (TimeLeft <= 10 && setup.time_limit)
12220           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12221
12222 #if 1
12223         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12224            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12225
12226         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12227
12228         DisplayGameControlValues();
12229 #else
12230         DrawGameValue_Time(TimeLeft);
12231 #endif
12232
12233         if (!TimeLeft && setup.time_limit)
12234         {
12235           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12236             level.native_em_level->lev->killed_out_of_time = TRUE;
12237           else
12238             for (i = 0; i < MAX_PLAYERS; i++)
12239               KillPlayer(&stored_player[i]);
12240         }
12241       }
12242 #if 1
12243       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12244       {
12245         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12246
12247         DisplayGameControlValues();
12248       }
12249 #else
12250       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12251         DrawGameValue_Time(TimePlayed);
12252 #endif
12253
12254       level.native_em_level->lev->time =
12255         (game.no_time_limit ? TimePlayed : TimeLeft);
12256     }
12257
12258     if (tape.recording || tape.playing)
12259       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12260   }
12261
12262 #if 1
12263   UpdateAndDisplayGameControlValues();
12264 #else
12265   UpdateGameDoorValues();
12266   DrawGameDoorValues();
12267 #endif
12268 }
12269
12270 void AdvanceFrameAndPlayerCounters(int player_nr)
12271 {
12272   int i;
12273
12274   /* advance frame counters (global frame counter and time frame counter) */
12275   FrameCounter++;
12276   TimeFrames++;
12277
12278   /* advance player counters (counters for move delay, move animation etc.) */
12279   for (i = 0; i < MAX_PLAYERS; i++)
12280   {
12281     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12282     int move_delay_value = stored_player[i].move_delay_value;
12283     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12284
12285     if (!advance_player_counters)       /* not all players may be affected */
12286       continue;
12287
12288 #if USE_NEW_PLAYER_ANIM
12289     if (move_frames == 0)       /* less than one move per game frame */
12290     {
12291       int stepsize = TILEX / move_delay_value;
12292       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12293       int count = (stored_player[i].is_moving ?
12294                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12295
12296       if (count % delay == 0)
12297         move_frames = 1;
12298     }
12299 #endif
12300
12301     stored_player[i].Frame += move_frames;
12302
12303     if (stored_player[i].MovPos != 0)
12304       stored_player[i].StepFrame += move_frames;
12305
12306     if (stored_player[i].move_delay > 0)
12307       stored_player[i].move_delay--;
12308
12309     /* due to bugs in previous versions, counter must count up, not down */
12310     if (stored_player[i].push_delay != -1)
12311       stored_player[i].push_delay++;
12312
12313     if (stored_player[i].drop_delay > 0)
12314       stored_player[i].drop_delay--;
12315
12316     if (stored_player[i].is_dropping_pressed)
12317       stored_player[i].drop_pressed_delay++;
12318   }
12319 }
12320
12321 void StartGameActions(boolean init_network_game, boolean record_tape,
12322                       int random_seed)
12323 {
12324   unsigned int new_random_seed = InitRND(random_seed);
12325
12326   if (record_tape)
12327     TapeStartRecording(new_random_seed);
12328
12329 #if defined(NETWORK_AVALIABLE)
12330   if (init_network_game)
12331   {
12332     SendToServer_StartPlaying();
12333
12334     return;
12335   }
12336 #endif
12337
12338   InitGame();
12339 }
12340
12341 void GameActions()
12342 {
12343   static unsigned int game_frame_delay = 0;
12344   unsigned int game_frame_delay_value;
12345   byte *recorded_player_action;
12346   byte summarized_player_action = 0;
12347   byte tape_action[MAX_PLAYERS];
12348   int i;
12349
12350   /* detect endless loops, caused by custom element programming */
12351   if (recursion_loop_detected && recursion_loop_depth == 0)
12352   {
12353     char *message = getStringCat3("Internal Error ! Element ",
12354                                   EL_NAME(recursion_loop_element),
12355                                   " caused endless loop ! Quit the game ?");
12356
12357     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12358           EL_NAME(recursion_loop_element));
12359
12360     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12361
12362     recursion_loop_detected = FALSE;    /* if game should be continued */
12363
12364     free(message);
12365
12366     return;
12367   }
12368
12369   if (game.restart_level)
12370     StartGameActions(options.network, setup.autorecord, level.random_seed);
12371
12372   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12373   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12374   {
12375     if (level.native_em_level->lev->home == 0)  /* all players at home */
12376     {
12377       PlayerWins(local_player);
12378
12379       AllPlayersGone = TRUE;
12380
12381       level.native_em_level->lev->home = -1;
12382     }
12383
12384     if (level.native_em_level->ply[0]->alive == 0 &&
12385         level.native_em_level->ply[1]->alive == 0 &&
12386         level.native_em_level->ply[2]->alive == 0 &&
12387         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12388       AllPlayersGone = TRUE;
12389   }
12390   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12391   {
12392     if (game_sp.LevelSolved &&
12393         !game_sp.GameOver)                              /* game won */
12394     {
12395       PlayerWins(local_player);
12396
12397       game_sp.GameOver = TRUE;
12398
12399       AllPlayersGone = TRUE;
12400     }
12401
12402     if (game_sp.GameOver)                               /* game lost */
12403       AllPlayersGone = TRUE;
12404   }
12405
12406   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12407     GameWon();
12408
12409   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12410     TapeStop();
12411
12412   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12413     return;
12414
12415   game_frame_delay_value =
12416     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12417
12418   if (tape.playing && tape.warp_forward && !tape.pausing)
12419     game_frame_delay_value = 0;
12420
12421   /* ---------- main game synchronization point ---------- */
12422
12423   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12424
12425   if (network_playing && !network_player_action_received)
12426   {
12427     /* try to get network player actions in time */
12428
12429 #if defined(NETWORK_AVALIABLE)
12430     /* last chance to get network player actions without main loop delay */
12431     HandleNetworking();
12432 #endif
12433
12434     /* game was quit by network peer */
12435     if (game_status != GAME_MODE_PLAYING)
12436       return;
12437
12438     if (!network_player_action_received)
12439       return;           /* failed to get network player actions in time */
12440
12441     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12442   }
12443
12444   if (tape.pausing)
12445     return;
12446
12447   /* at this point we know that we really continue executing the game */
12448
12449   network_player_action_received = FALSE;
12450
12451   /* when playing tape, read previously recorded player input from tape data */
12452   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12453
12454 #if 1
12455   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12456   if (tape.pausing)
12457     return;
12458 #endif
12459
12460   if (tape.set_centered_player)
12461   {
12462     game.centered_player_nr_next = tape.centered_player_nr_next;
12463     game.set_centered_player = TRUE;
12464   }
12465
12466   for (i = 0; i < MAX_PLAYERS; i++)
12467   {
12468     summarized_player_action |= stored_player[i].action;
12469
12470     if (!network_playing)
12471       stored_player[i].effective_action = stored_player[i].action;
12472   }
12473
12474 #if defined(NETWORK_AVALIABLE)
12475   if (network_playing)
12476     SendToServer_MovePlayer(summarized_player_action);
12477 #endif
12478
12479   if (!options.network && !setup.team_mode)
12480     local_player->effective_action = summarized_player_action;
12481
12482   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12483   {
12484     for (i = 0; i < MAX_PLAYERS; i++)
12485       stored_player[i].effective_action =
12486         (i == game.centered_player_nr ? summarized_player_action : 0);
12487   }
12488
12489   if (recorded_player_action != NULL)
12490     for (i = 0; i < MAX_PLAYERS; i++)
12491       stored_player[i].effective_action = recorded_player_action[i];
12492
12493   for (i = 0; i < MAX_PLAYERS; i++)
12494   {
12495     tape_action[i] = stored_player[i].effective_action;
12496
12497     /* (this can only happen in the R'n'D game engine) */
12498     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12499       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12500   }
12501
12502   /* only record actions from input devices, but not programmed actions */
12503   if (tape.recording)
12504     TapeRecordAction(tape_action);
12505
12506 #if USE_NEW_PLAYER_ASSIGNMENTS
12507   {
12508     byte mapped_action[MAX_PLAYERS];
12509
12510     for (i = 0; i < MAX_PLAYERS; i++)
12511       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12512
12513     for (i = 0; i < MAX_PLAYERS; i++)
12514       stored_player[i].effective_action = mapped_action[i];
12515   }
12516 #endif
12517
12518   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12519   {
12520     GameActions_EM_Main();
12521   }
12522   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12523   {
12524     GameActions_SP_Main();
12525   }
12526   else
12527   {
12528     GameActions_RND();
12529   }
12530 }
12531
12532 void GameActions_EM_Main()
12533 {
12534   byte effective_action[MAX_PLAYERS];
12535   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12536   int i;
12537
12538   for (i = 0; i < MAX_PLAYERS; i++)
12539     effective_action[i] = stored_player[i].effective_action;
12540
12541   GameActions_EM(effective_action, warp_mode);
12542
12543   CheckLevelTime();
12544
12545   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12546 }
12547
12548 void GameActions_SP_Main()
12549 {
12550   byte effective_action[MAX_PLAYERS];
12551   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12552   int i;
12553
12554   for (i = 0; i < MAX_PLAYERS; i++)
12555     effective_action[i] = stored_player[i].effective_action;
12556
12557   GameActions_SP(effective_action, warp_mode);
12558
12559   CheckLevelTime();
12560
12561   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12562 }
12563
12564 void GameActions_RND()
12565 {
12566   int magic_wall_x = 0, magic_wall_y = 0;
12567   int i, x, y, element, graphic;
12568
12569   InitPlayfieldScanModeVars();
12570
12571 #if USE_ONE_MORE_CHANGE_PER_FRAME
12572   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12573   {
12574     SCAN_PLAYFIELD(x, y)
12575     {
12576       ChangeCount[x][y] = 0;
12577       ChangeEvent[x][y] = -1;
12578     }
12579   }
12580 #endif
12581
12582   if (game.set_centered_player)
12583   {
12584     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12585
12586     /* switching to "all players" only possible if all players fit to screen */
12587     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12588     {
12589       game.centered_player_nr_next = game.centered_player_nr;
12590       game.set_centered_player = FALSE;
12591     }
12592
12593     /* do not switch focus to non-existing (or non-active) player */
12594     if (game.centered_player_nr_next >= 0 &&
12595         !stored_player[game.centered_player_nr_next].active)
12596     {
12597       game.centered_player_nr_next = game.centered_player_nr;
12598       game.set_centered_player = FALSE;
12599     }
12600   }
12601
12602   if (game.set_centered_player &&
12603       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12604   {
12605     int sx, sy;
12606
12607     if (game.centered_player_nr_next == -1)
12608     {
12609       setScreenCenteredToAllPlayers(&sx, &sy);
12610     }
12611     else
12612     {
12613       sx = stored_player[game.centered_player_nr_next].jx;
12614       sy = stored_player[game.centered_player_nr_next].jy;
12615     }
12616
12617     game.centered_player_nr = game.centered_player_nr_next;
12618     game.set_centered_player = FALSE;
12619
12620     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12621     DrawGameDoorValues();
12622   }
12623
12624   for (i = 0; i < MAX_PLAYERS; i++)
12625   {
12626     int actual_player_action = stored_player[i].effective_action;
12627
12628 #if 1
12629     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12630        - rnd_equinox_tetrachloride 048
12631        - rnd_equinox_tetrachloride_ii 096
12632        - rnd_emanuel_schmieg 002
12633        - doctor_sloan_ww 001, 020
12634     */
12635     if (stored_player[i].MovPos == 0)
12636       CheckGravityMovement(&stored_player[i]);
12637 #endif
12638
12639     /* overwrite programmed action with tape action */
12640     if (stored_player[i].programmed_action)
12641       actual_player_action = stored_player[i].programmed_action;
12642
12643     PlayerActions(&stored_player[i], actual_player_action);
12644
12645     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12646   }
12647
12648   ScrollScreen(NULL, SCROLL_GO_ON);
12649
12650   /* for backwards compatibility, the following code emulates a fixed bug that
12651      occured when pushing elements (causing elements that just made their last
12652      pushing step to already (if possible) make their first falling step in the
12653      same game frame, which is bad); this code is also needed to use the famous
12654      "spring push bug" which is used in older levels and might be wanted to be
12655      used also in newer levels, but in this case the buggy pushing code is only
12656      affecting the "spring" element and no other elements */
12657
12658   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12659   {
12660     for (i = 0; i < MAX_PLAYERS; i++)
12661     {
12662       struct PlayerInfo *player = &stored_player[i];
12663       int x = player->jx;
12664       int y = player->jy;
12665
12666       if (player->active && player->is_pushing && player->is_moving &&
12667           IS_MOVING(x, y) &&
12668           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12669            Feld[x][y] == EL_SPRING))
12670       {
12671         ContinueMoving(x, y);
12672
12673         /* continue moving after pushing (this is actually a bug) */
12674         if (!IS_MOVING(x, y))
12675           Stop[x][y] = FALSE;
12676       }
12677     }
12678   }
12679
12680 #if 0
12681   debug_print_timestamp(0, "start main loop profiling");
12682 #endif
12683
12684   SCAN_PLAYFIELD(x, y)
12685   {
12686     ChangeCount[x][y] = 0;
12687     ChangeEvent[x][y] = -1;
12688
12689     /* this must be handled before main playfield loop */
12690     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12691     {
12692       MovDelay[x][y]--;
12693       if (MovDelay[x][y] <= 0)
12694         RemoveField(x, y);
12695     }
12696
12697 #if USE_NEW_SNAP_DELAY
12698     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12699     {
12700       MovDelay[x][y]--;
12701       if (MovDelay[x][y] <= 0)
12702       {
12703         RemoveField(x, y);
12704         TEST_DrawLevelField(x, y);
12705
12706         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12707       }
12708     }
12709 #endif
12710
12711 #if DEBUG
12712     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12713     {
12714       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12715       printf("GameActions(): This should never happen!\n");
12716
12717       ChangePage[x][y] = -1;
12718     }
12719 #endif
12720
12721     Stop[x][y] = FALSE;
12722     if (WasJustMoving[x][y] > 0)
12723       WasJustMoving[x][y]--;
12724     if (WasJustFalling[x][y] > 0)
12725       WasJustFalling[x][y]--;
12726     if (CheckCollision[x][y] > 0)
12727       CheckCollision[x][y]--;
12728     if (CheckImpact[x][y] > 0)
12729       CheckImpact[x][y]--;
12730
12731     GfxFrame[x][y]++;
12732
12733     /* reset finished pushing action (not done in ContinueMoving() to allow
12734        continuous pushing animation for elements with zero push delay) */
12735     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12736     {
12737       ResetGfxAnimation(x, y);
12738       TEST_DrawLevelField(x, y);
12739     }
12740
12741 #if DEBUG
12742     if (IS_BLOCKED(x, y))
12743     {
12744       int oldx, oldy;
12745
12746       Blocked2Moving(x, y, &oldx, &oldy);
12747       if (!IS_MOVING(oldx, oldy))
12748       {
12749         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12750         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12751         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12752         printf("GameActions(): This should never happen!\n");
12753       }
12754     }
12755 #endif
12756   }
12757
12758 #if 0
12759   debug_print_timestamp(0, "- time for pre-main loop:");
12760 #endif
12761
12762 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12763   SCAN_PLAYFIELD(x, y)
12764   {
12765     element = Feld[x][y];
12766     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12767
12768 #if 1
12769     {
12770 #if 1
12771       int element2 = element;
12772       int graphic2 = graphic;
12773 #else
12774       int element2 = Feld[x][y];
12775       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12776 #endif
12777       int last_gfx_frame = GfxFrame[x][y];
12778
12779       if (graphic_info[graphic2].anim_global_sync)
12780         GfxFrame[x][y] = FrameCounter;
12781       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12782         GfxFrame[x][y] = CustomValue[x][y];
12783       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12784         GfxFrame[x][y] = element_info[element2].collect_score;
12785       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12786         GfxFrame[x][y] = ChangeDelay[x][y];
12787
12788       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12789         DrawLevelGraphicAnimation(x, y, graphic2);
12790     }
12791 #else
12792     ResetGfxFrame(x, y, TRUE);
12793 #endif
12794
12795 #if 1
12796     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12797         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12798       ResetRandomAnimationValue(x, y);
12799 #endif
12800
12801 #if 1
12802     SetRandomAnimationValue(x, y);
12803 #endif
12804
12805 #if 1
12806     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12807 #endif
12808   }
12809 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12810
12811 #if 0
12812   debug_print_timestamp(0, "- time for TEST loop:     -->");
12813 #endif
12814
12815   SCAN_PLAYFIELD(x, y)
12816   {
12817     element = Feld[x][y];
12818     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12819
12820     ResetGfxFrame(x, y, TRUE);
12821
12822     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12823         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12824       ResetRandomAnimationValue(x, y);
12825
12826     SetRandomAnimationValue(x, y);
12827
12828     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12829
12830     if (IS_INACTIVE(element))
12831     {
12832       if (IS_ANIMATED(graphic))
12833         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12834
12835       continue;
12836     }
12837
12838     /* this may take place after moving, so 'element' may have changed */
12839     if (IS_CHANGING(x, y) &&
12840         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12841     {
12842       int page = element_info[element].event_page_nr[CE_DELAY];
12843
12844 #if 1
12845       HandleElementChange(x, y, page);
12846 #else
12847       if (CAN_CHANGE(element))
12848         HandleElementChange(x, y, page);
12849
12850       if (HAS_ACTION(element))
12851         ExecuteCustomElementAction(x, y, element, page);
12852 #endif
12853
12854       element = Feld[x][y];
12855       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12856     }
12857
12858 #if 0   // ---------------------------------------------------------------------
12859
12860     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12861     {
12862       StartMoving(x, y);
12863
12864       element = Feld[x][y];
12865       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12866
12867       if (IS_ANIMATED(graphic) &&
12868           !IS_MOVING(x, y) &&
12869           !Stop[x][y])
12870         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12871
12872       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12873         TEST_DrawTwinkleOnField(x, y);
12874     }
12875     else if (IS_MOVING(x, y))
12876       ContinueMoving(x, y);
12877     else
12878     {
12879       switch (element)
12880       {
12881         case EL_ACID:
12882         case EL_EXIT_OPEN:
12883         case EL_EM_EXIT_OPEN:
12884         case EL_SP_EXIT_OPEN:
12885         case EL_STEEL_EXIT_OPEN:
12886         case EL_EM_STEEL_EXIT_OPEN:
12887         case EL_SP_TERMINAL:
12888         case EL_SP_TERMINAL_ACTIVE:
12889         case EL_EXTRA_TIME:
12890         case EL_SHIELD_NORMAL:
12891         case EL_SHIELD_DEADLY:
12892           if (IS_ANIMATED(graphic))
12893             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12894           break;
12895
12896         case EL_DYNAMITE_ACTIVE:
12897         case EL_EM_DYNAMITE_ACTIVE:
12898         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12899         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12900         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12901         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12902         case EL_SP_DISK_RED_ACTIVE:
12903           CheckDynamite(x, y);
12904           break;
12905
12906         case EL_AMOEBA_GROWING:
12907           AmoebeWaechst(x, y);
12908           break;
12909
12910         case EL_AMOEBA_SHRINKING:
12911           AmoebaDisappearing(x, y);
12912           break;
12913
12914 #if !USE_NEW_AMOEBA_CODE
12915         case EL_AMOEBA_WET:
12916         case EL_AMOEBA_DRY:
12917         case EL_AMOEBA_FULL:
12918         case EL_BD_AMOEBA:
12919         case EL_EMC_DRIPPER:
12920           AmoebeAbleger(x, y);
12921           break;
12922 #endif
12923
12924         case EL_GAME_OF_LIFE:
12925         case EL_BIOMAZE:
12926           Life(x, y);
12927           break;
12928
12929         case EL_EXIT_CLOSED:
12930           CheckExit(x, y);
12931           break;
12932
12933         case EL_EM_EXIT_CLOSED:
12934           CheckExitEM(x, y);
12935           break;
12936
12937         case EL_STEEL_EXIT_CLOSED:
12938           CheckExitSteel(x, y);
12939           break;
12940
12941         case EL_EM_STEEL_EXIT_CLOSED:
12942           CheckExitSteelEM(x, y);
12943           break;
12944
12945         case EL_SP_EXIT_CLOSED:
12946           CheckExitSP(x, y);
12947           break;
12948
12949         case EL_EXPANDABLE_WALL_GROWING:
12950         case EL_EXPANDABLE_STEELWALL_GROWING:
12951           MauerWaechst(x, y);
12952           break;
12953
12954         case EL_EXPANDABLE_WALL:
12955         case EL_EXPANDABLE_WALL_HORIZONTAL:
12956         case EL_EXPANDABLE_WALL_VERTICAL:
12957         case EL_EXPANDABLE_WALL_ANY:
12958         case EL_BD_EXPANDABLE_WALL:
12959           MauerAbleger(x, y);
12960           break;
12961
12962         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12963         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12964         case EL_EXPANDABLE_STEELWALL_ANY:
12965           MauerAblegerStahl(x, y);
12966           break;
12967
12968         case EL_FLAMES:
12969           CheckForDragon(x, y);
12970           break;
12971
12972         case EL_EXPLOSION:
12973           break;
12974
12975         case EL_ELEMENT_SNAPPING:
12976         case EL_DIAGONAL_SHRINKING:
12977         case EL_DIAGONAL_GROWING:
12978         {
12979           graphic =
12980             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12981
12982           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12983           break;
12984         }
12985
12986         default:
12987           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12988             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12989           break;
12990       }
12991     }
12992
12993 #else   // ---------------------------------------------------------------------
12994
12995     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12996     {
12997       StartMoving(x, y);
12998
12999       element = Feld[x][y];
13000       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13001
13002       if (IS_ANIMATED(graphic) &&
13003           !IS_MOVING(x, y) &&
13004           !Stop[x][y])
13005         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13006
13007       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13008         TEST_DrawTwinkleOnField(x, y);
13009     }
13010     else if ((element == EL_ACID ||
13011               element == EL_EXIT_OPEN ||
13012               element == EL_EM_EXIT_OPEN ||
13013               element == EL_SP_EXIT_OPEN ||
13014               element == EL_STEEL_EXIT_OPEN ||
13015               element == EL_EM_STEEL_EXIT_OPEN ||
13016               element == EL_SP_TERMINAL ||
13017               element == EL_SP_TERMINAL_ACTIVE ||
13018               element == EL_EXTRA_TIME ||
13019               element == EL_SHIELD_NORMAL ||
13020               element == EL_SHIELD_DEADLY) &&
13021              IS_ANIMATED(graphic))
13022       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13023     else if (IS_MOVING(x, y))
13024       ContinueMoving(x, y);
13025     else if (IS_ACTIVE_BOMB(element))
13026       CheckDynamite(x, y);
13027     else if (element == EL_AMOEBA_GROWING)
13028       AmoebeWaechst(x, y);
13029     else if (element == EL_AMOEBA_SHRINKING)
13030       AmoebaDisappearing(x, y);
13031
13032 #if !USE_NEW_AMOEBA_CODE
13033     else if (IS_AMOEBALIVE(element))
13034       AmoebeAbleger(x, y);
13035 #endif
13036
13037     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13038       Life(x, y);
13039     else if (element == EL_EXIT_CLOSED)
13040       CheckExit(x, y);
13041     else if (element == EL_EM_EXIT_CLOSED)
13042       CheckExitEM(x, y);
13043     else if (element == EL_STEEL_EXIT_CLOSED)
13044       CheckExitSteel(x, y);
13045     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13046       CheckExitSteelEM(x, y);
13047     else if (element == EL_SP_EXIT_CLOSED)
13048       CheckExitSP(x, y);
13049     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13050              element == EL_EXPANDABLE_STEELWALL_GROWING)
13051       MauerWaechst(x, y);
13052     else if (element == EL_EXPANDABLE_WALL ||
13053              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13054              element == EL_EXPANDABLE_WALL_VERTICAL ||
13055              element == EL_EXPANDABLE_WALL_ANY ||
13056              element == EL_BD_EXPANDABLE_WALL)
13057       MauerAbleger(x, y);
13058     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13059              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13060              element == EL_EXPANDABLE_STEELWALL_ANY)
13061       MauerAblegerStahl(x, y);
13062     else if (element == EL_FLAMES)
13063       CheckForDragon(x, y);
13064     else if (element == EL_EXPLOSION)
13065       ; /* drawing of correct explosion animation is handled separately */
13066     else if (element == EL_ELEMENT_SNAPPING ||
13067              element == EL_DIAGONAL_SHRINKING ||
13068              element == EL_DIAGONAL_GROWING)
13069     {
13070       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13071
13072       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13073     }
13074     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13075       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13076
13077 #endif  // ---------------------------------------------------------------------
13078
13079     if (IS_BELT_ACTIVE(element))
13080       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13081
13082     if (game.magic_wall_active)
13083     {
13084       int jx = local_player->jx, jy = local_player->jy;
13085
13086       /* play the element sound at the position nearest to the player */
13087       if ((element == EL_MAGIC_WALL_FULL ||
13088            element == EL_MAGIC_WALL_ACTIVE ||
13089            element == EL_MAGIC_WALL_EMPTYING ||
13090            element == EL_BD_MAGIC_WALL_FULL ||
13091            element == EL_BD_MAGIC_WALL_ACTIVE ||
13092            element == EL_BD_MAGIC_WALL_EMPTYING ||
13093            element == EL_DC_MAGIC_WALL_FULL ||
13094            element == EL_DC_MAGIC_WALL_ACTIVE ||
13095            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13096           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13097       {
13098         magic_wall_x = x;
13099         magic_wall_y = y;
13100       }
13101     }
13102   }
13103
13104 #if 0
13105   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13106 #endif
13107
13108 #if USE_NEW_AMOEBA_CODE
13109   /* new experimental amoeba growth stuff */
13110   if (!(FrameCounter % 8))
13111   {
13112     static unsigned int random = 1684108901;
13113
13114     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13115     {
13116       x = RND(lev_fieldx);
13117       y = RND(lev_fieldy);
13118       element = Feld[x][y];
13119
13120       if (!IS_PLAYER(x,y) &&
13121           (element == EL_EMPTY ||
13122            CAN_GROW_INTO(element) ||
13123            element == EL_QUICKSAND_EMPTY ||
13124            element == EL_QUICKSAND_FAST_EMPTY ||
13125            element == EL_ACID_SPLASH_LEFT ||
13126            element == EL_ACID_SPLASH_RIGHT))
13127       {
13128         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13129             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13130             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13131             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13132           Feld[x][y] = EL_AMOEBA_DROP;
13133       }
13134
13135       random = random * 129 + 1;
13136     }
13137   }
13138 #endif
13139
13140 #if 0
13141   if (game.explosions_delayed)
13142 #endif
13143   {
13144     game.explosions_delayed = FALSE;
13145
13146     SCAN_PLAYFIELD(x, y)
13147     {
13148       element = Feld[x][y];
13149
13150       if (ExplodeField[x][y])
13151         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13152       else if (element == EL_EXPLOSION)
13153         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13154
13155       ExplodeField[x][y] = EX_TYPE_NONE;
13156     }
13157
13158     game.explosions_delayed = TRUE;
13159   }
13160
13161   if (game.magic_wall_active)
13162   {
13163     if (!(game.magic_wall_time_left % 4))
13164     {
13165       int element = Feld[magic_wall_x][magic_wall_y];
13166
13167       if (element == EL_BD_MAGIC_WALL_FULL ||
13168           element == EL_BD_MAGIC_WALL_ACTIVE ||
13169           element == EL_BD_MAGIC_WALL_EMPTYING)
13170         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13171       else if (element == EL_DC_MAGIC_WALL_FULL ||
13172                element == EL_DC_MAGIC_WALL_ACTIVE ||
13173                element == EL_DC_MAGIC_WALL_EMPTYING)
13174         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13175       else
13176         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13177     }
13178
13179     if (game.magic_wall_time_left > 0)
13180     {
13181       game.magic_wall_time_left--;
13182
13183       if (!game.magic_wall_time_left)
13184       {
13185         SCAN_PLAYFIELD(x, y)
13186         {
13187           element = Feld[x][y];
13188
13189           if (element == EL_MAGIC_WALL_ACTIVE ||
13190               element == EL_MAGIC_WALL_FULL)
13191           {
13192             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13193             TEST_DrawLevelField(x, y);
13194           }
13195           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13196                    element == EL_BD_MAGIC_WALL_FULL)
13197           {
13198             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13199             TEST_DrawLevelField(x, y);
13200           }
13201           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13202                    element == EL_DC_MAGIC_WALL_FULL)
13203           {
13204             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13205             TEST_DrawLevelField(x, y);
13206           }
13207         }
13208
13209         game.magic_wall_active = FALSE;
13210       }
13211     }
13212   }
13213
13214   if (game.light_time_left > 0)
13215   {
13216     game.light_time_left--;
13217
13218     if (game.light_time_left == 0)
13219       RedrawAllLightSwitchesAndInvisibleElements();
13220   }
13221
13222   if (game.timegate_time_left > 0)
13223   {
13224     game.timegate_time_left--;
13225
13226     if (game.timegate_time_left == 0)
13227       CloseAllOpenTimegates();
13228   }
13229
13230   if (game.lenses_time_left > 0)
13231   {
13232     game.lenses_time_left--;
13233
13234     if (game.lenses_time_left == 0)
13235       RedrawAllInvisibleElementsForLenses();
13236   }
13237
13238   if (game.magnify_time_left > 0)
13239   {
13240     game.magnify_time_left--;
13241
13242     if (game.magnify_time_left == 0)
13243       RedrawAllInvisibleElementsForMagnifier();
13244   }
13245
13246   for (i = 0; i < MAX_PLAYERS; i++)
13247   {
13248     struct PlayerInfo *player = &stored_player[i];
13249
13250     if (SHIELD_ON(player))
13251     {
13252       if (player->shield_deadly_time_left)
13253         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13254       else if (player->shield_normal_time_left)
13255         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13256     }
13257   }
13258
13259 #if USE_DELAYED_GFX_REDRAW
13260   SCAN_PLAYFIELD(x, y)
13261   {
13262 #if 1
13263     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13264 #else
13265     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13266         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13267 #endif
13268     {
13269       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13270          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13271
13272       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13273         DrawLevelField(x, y);
13274
13275       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13276         DrawLevelFieldCrumbled(x, y);
13277
13278       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13279         DrawLevelFieldCrumbledNeighbours(x, y);
13280
13281       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13282         DrawTwinkleOnField(x, y);
13283     }
13284
13285     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13286   }
13287 #endif
13288
13289   CheckLevelTime();
13290
13291   DrawAllPlayers();
13292   PlayAllPlayersSound();
13293
13294   if (options.debug)                    /* calculate frames per second */
13295   {
13296     static unsigned int fps_counter = 0;
13297     static int fps_frames = 0;
13298     unsigned int fps_delay_ms = Counter() - fps_counter;
13299
13300     fps_frames++;
13301
13302     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13303     {
13304       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13305
13306       fps_frames = 0;
13307       fps_counter = Counter();
13308     }
13309
13310     redraw_mask |= REDRAW_FPS;
13311   }
13312
13313   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13314
13315   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13316   {
13317     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13318
13319     local_player->show_envelope = 0;
13320   }
13321
13322 #if 0
13323   debug_print_timestamp(0, "stop main loop profiling ");
13324   printf("----------------------------------------------------------\n");
13325 #endif
13326
13327   /* use random number generator in every frame to make it less predictable */
13328   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13329     RND(1);
13330 }
13331
13332 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13333 {
13334   int min_x = x, min_y = y, max_x = x, max_y = y;
13335   int i;
13336
13337   for (i = 0; i < MAX_PLAYERS; i++)
13338   {
13339     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13340
13341     if (!stored_player[i].active || &stored_player[i] == player)
13342       continue;
13343
13344     min_x = MIN(min_x, jx);
13345     min_y = MIN(min_y, jy);
13346     max_x = MAX(max_x, jx);
13347     max_y = MAX(max_y, jy);
13348   }
13349
13350   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13351 }
13352
13353 static boolean AllPlayersInVisibleScreen()
13354 {
13355   int i;
13356
13357   for (i = 0; i < MAX_PLAYERS; i++)
13358   {
13359     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13360
13361     if (!stored_player[i].active)
13362       continue;
13363
13364     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13365       return FALSE;
13366   }
13367
13368   return TRUE;
13369 }
13370
13371 void ScrollLevel(int dx, int dy)
13372 {
13373 #if 0
13374   /* (directly solved in BlitBitmap() now) */
13375   static Bitmap *bitmap_db_field2 = NULL;
13376   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13377   int x, y;
13378 #else
13379   int x, y;
13380 #endif
13381
13382 #if 0
13383   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13384   /* only horizontal XOR vertical scroll direction allowed */
13385   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13386     return;
13387 #endif
13388
13389 #if 0
13390   /* (directly solved in BlitBitmap() now) */
13391   if (bitmap_db_field2 == NULL)
13392     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13393
13394   /* needed when blitting directly to same bitmap -- should not be needed with
13395      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13396   BlitBitmap(drawto_field, bitmap_db_field2,
13397              FX + TILEX * (dx == -1) - softscroll_offset,
13398              FY + TILEY * (dy == -1) - softscroll_offset,
13399              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13400              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13401              FX + TILEX * (dx == 1) - softscroll_offset,
13402              FY + TILEY * (dy == 1) - softscroll_offset);
13403   BlitBitmap(bitmap_db_field2, drawto_field,
13404              FX + TILEX * (dx == 1) - softscroll_offset,
13405              FY + TILEY * (dy == 1) - softscroll_offset,
13406              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13407              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13408              FX + TILEX * (dx == 1) - softscroll_offset,
13409              FY + TILEY * (dy == 1) - softscroll_offset);
13410
13411 #else
13412
13413 #if 0
13414   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13415   int xsize = (BX2 - BX1 + 1);
13416   int ysize = (BY2 - BY1 + 1);
13417   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13418   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13419   int step  = (start < end ? +1 : -1);
13420
13421   for (i = start; i != end; i += step)
13422   {
13423     BlitBitmap(drawto_field, drawto_field,
13424                FX + TILEX * (dx != 0 ? i + step : 0),
13425                FY + TILEY * (dy != 0 ? i + step : 0),
13426                TILEX * (dx != 0 ? 1 : xsize),
13427                TILEY * (dy != 0 ? 1 : ysize),
13428                FX + TILEX * (dx != 0 ? i : 0),
13429                FY + TILEY * (dy != 0 ? i : 0));
13430   }
13431
13432 #else
13433
13434 #if NEW_TILESIZE
13435 #if NEW_SCROLL
13436   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13437 #else
13438   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13439 #endif
13440 #else
13441 #if NEW_SCROLL
13442   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13443 #else
13444   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13445 #endif
13446 #endif
13447
13448 #if NEW_TILESIZE
13449   BlitBitmap(drawto_field, drawto_field,
13450              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13451              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13452              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13453              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13454              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13455              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13456 #else
13457   BlitBitmap(drawto_field, drawto_field,
13458              FX + TILEX * (dx == -1) - softscroll_offset,
13459              FY + TILEY * (dy == -1) - softscroll_offset,
13460              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13461              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13462              FX + TILEX * (dx == 1) - softscroll_offset,
13463              FY + TILEY * (dy == 1) - softscroll_offset);
13464 #endif
13465
13466 #endif
13467 #endif
13468
13469   if (dx != 0)
13470   {
13471     x = (dx == 1 ? BX1 : BX2);
13472     for (y = BY1; y <= BY2; y++)
13473       DrawScreenField(x, y);
13474   }
13475
13476   if (dy != 0)
13477   {
13478     y = (dy == 1 ? BY1 : BY2);
13479     for (x = BX1; x <= BX2; x++)
13480       DrawScreenField(x, y);
13481   }
13482
13483   redraw_mask |= REDRAW_FIELD;
13484 }
13485
13486 static boolean canFallDown(struct PlayerInfo *player)
13487 {
13488   int jx = player->jx, jy = player->jy;
13489
13490   return (IN_LEV_FIELD(jx, jy + 1) &&
13491           (IS_FREE(jx, jy + 1) ||
13492            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13493           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13494           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13495 }
13496
13497 static boolean canPassField(int x, int y, int move_dir)
13498 {
13499   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13500   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13501   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13502   int nextx = x + dx;
13503   int nexty = y + dy;
13504   int element = Feld[x][y];
13505
13506   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13507           !CAN_MOVE(element) &&
13508           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13509           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13510           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13511 }
13512
13513 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13514 {
13515   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13516   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13517   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13518   int newx = x + dx;
13519   int newy = y + dy;
13520
13521   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13522           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13523           (IS_DIGGABLE(Feld[newx][newy]) ||
13524            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13525            canPassField(newx, newy, move_dir)));
13526 }
13527
13528 static void CheckGravityMovement(struct PlayerInfo *player)
13529 {
13530 #if USE_PLAYER_GRAVITY
13531   if (player->gravity && !player->programmed_action)
13532 #else
13533   if (game.gravity && !player->programmed_action)
13534 #endif
13535   {
13536     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13537     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13538     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13539     int jx = player->jx, jy = player->jy;
13540     boolean player_is_moving_to_valid_field =
13541       (!player_is_snapping &&
13542        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13543         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13544     boolean player_can_fall_down = canFallDown(player);
13545
13546     if (player_can_fall_down &&
13547         !player_is_moving_to_valid_field)
13548       player->programmed_action = MV_DOWN;
13549   }
13550 }
13551
13552 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13553 {
13554   return CheckGravityMovement(player);
13555
13556 #if USE_PLAYER_GRAVITY
13557   if (player->gravity && !player->programmed_action)
13558 #else
13559   if (game.gravity && !player->programmed_action)
13560 #endif
13561   {
13562     int jx = player->jx, jy = player->jy;
13563     boolean field_under_player_is_free =
13564       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13565     boolean player_is_standing_on_valid_field =
13566       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13567        (IS_WALKABLE(Feld[jx][jy]) &&
13568         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13569
13570     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13571       player->programmed_action = MV_DOWN;
13572   }
13573 }
13574
13575 /*
13576   MovePlayerOneStep()
13577   -----------------------------------------------------------------------------
13578   dx, dy:               direction (non-diagonal) to try to move the player to
13579   real_dx, real_dy:     direction as read from input device (can be diagonal)
13580 */
13581
13582 boolean MovePlayerOneStep(struct PlayerInfo *player,
13583                           int dx, int dy, int real_dx, int real_dy)
13584 {
13585   int jx = player->jx, jy = player->jy;
13586   int new_jx = jx + dx, new_jy = jy + dy;
13587 #if !USE_FIXED_DONT_RUN_INTO
13588   int element;
13589 #endif
13590   int can_move;
13591   boolean player_can_move = !player->cannot_move;
13592
13593   if (!player->active || (!dx && !dy))
13594     return MP_NO_ACTION;
13595
13596   player->MovDir = (dx < 0 ? MV_LEFT :
13597                     dx > 0 ? MV_RIGHT :
13598                     dy < 0 ? MV_UP :
13599                     dy > 0 ? MV_DOWN :  MV_NONE);
13600
13601   if (!IN_LEV_FIELD(new_jx, new_jy))
13602     return MP_NO_ACTION;
13603
13604   if (!player_can_move)
13605   {
13606     if (player->MovPos == 0)
13607     {
13608       player->is_moving = FALSE;
13609       player->is_digging = FALSE;
13610       player->is_collecting = FALSE;
13611       player->is_snapping = FALSE;
13612       player->is_pushing = FALSE;
13613     }
13614   }
13615
13616 #if 1
13617   if (!options.network && game.centered_player_nr == -1 &&
13618       !AllPlayersInSight(player, new_jx, new_jy))
13619     return MP_NO_ACTION;
13620 #else
13621   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13622     return MP_NO_ACTION;
13623 #endif
13624
13625 #if !USE_FIXED_DONT_RUN_INTO
13626   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13627
13628   /* (moved to DigField()) */
13629   if (player_can_move && DONT_RUN_INTO(element))
13630   {
13631     if (element == EL_ACID && dx == 0 && dy == 1)
13632     {
13633       SplashAcid(new_jx, new_jy);
13634       Feld[jx][jy] = EL_PLAYER_1;
13635       InitMovingField(jx, jy, MV_DOWN);
13636       Store[jx][jy] = EL_ACID;
13637       ContinueMoving(jx, jy);
13638       BuryPlayer(player);
13639     }
13640     else
13641       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13642
13643     return MP_MOVING;
13644   }
13645 #endif
13646
13647   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13648   if (can_move != MP_MOVING)
13649     return can_move;
13650
13651   /* check if DigField() has caused relocation of the player */
13652   if (player->jx != jx || player->jy != jy)
13653     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13654
13655   StorePlayer[jx][jy] = 0;
13656   player->last_jx = jx;
13657   player->last_jy = jy;
13658   player->jx = new_jx;
13659   player->jy = new_jy;
13660   StorePlayer[new_jx][new_jy] = player->element_nr;
13661
13662   if (player->move_delay_value_next != -1)
13663   {
13664     player->move_delay_value = player->move_delay_value_next;
13665     player->move_delay_value_next = -1;
13666   }
13667
13668   player->MovPos =
13669     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13670
13671   player->step_counter++;
13672
13673   PlayerVisit[jx][jy] = FrameCounter;
13674
13675 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13676   player->is_moving = TRUE;
13677 #endif
13678
13679 #if 1
13680   /* should better be called in MovePlayer(), but this breaks some tapes */
13681   ScrollPlayer(player, SCROLL_INIT);
13682 #endif
13683
13684   return MP_MOVING;
13685 }
13686
13687 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13688 {
13689   int jx = player->jx, jy = player->jy;
13690   int old_jx = jx, old_jy = jy;
13691   int moved = MP_NO_ACTION;
13692
13693   if (!player->active)
13694     return FALSE;
13695
13696   if (!dx && !dy)
13697   {
13698     if (player->MovPos == 0)
13699     {
13700       player->is_moving = FALSE;
13701       player->is_digging = FALSE;
13702       player->is_collecting = FALSE;
13703       player->is_snapping = FALSE;
13704       player->is_pushing = FALSE;
13705     }
13706
13707     return FALSE;
13708   }
13709
13710   if (player->move_delay > 0)
13711     return FALSE;
13712
13713   player->move_delay = -1;              /* set to "uninitialized" value */
13714
13715   /* store if player is automatically moved to next field */
13716   player->is_auto_moving = (player->programmed_action != MV_NONE);
13717
13718   /* remove the last programmed player action */
13719   player->programmed_action = 0;
13720
13721   if (player->MovPos)
13722   {
13723     /* should only happen if pre-1.2 tape recordings are played */
13724     /* this is only for backward compatibility */
13725
13726     int original_move_delay_value = player->move_delay_value;
13727
13728 #if DEBUG
13729     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13730            tape.counter);
13731 #endif
13732
13733     /* scroll remaining steps with finest movement resolution */
13734     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13735
13736     while (player->MovPos)
13737     {
13738       ScrollPlayer(player, SCROLL_GO_ON);
13739       ScrollScreen(NULL, SCROLL_GO_ON);
13740
13741       AdvanceFrameAndPlayerCounters(player->index_nr);
13742
13743       DrawAllPlayers();
13744       BackToFront();
13745     }
13746
13747     player->move_delay_value = original_move_delay_value;
13748   }
13749
13750   player->is_active = FALSE;
13751
13752   if (player->last_move_dir & MV_HORIZONTAL)
13753   {
13754     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13755       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13756   }
13757   else
13758   {
13759     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13760       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13761   }
13762
13763 #if USE_FIXED_BORDER_RUNNING_GFX
13764   if (!moved && !player->is_active)
13765   {
13766     player->is_moving = FALSE;
13767     player->is_digging = FALSE;
13768     player->is_collecting = FALSE;
13769     player->is_snapping = FALSE;
13770     player->is_pushing = FALSE;
13771   }
13772 #endif
13773
13774   jx = player->jx;
13775   jy = player->jy;
13776
13777 #if 1
13778   if (moved & MP_MOVING && !ScreenMovPos &&
13779       (player->index_nr == game.centered_player_nr ||
13780        game.centered_player_nr == -1))
13781 #else
13782   if (moved & MP_MOVING && !ScreenMovPos &&
13783       (player == local_player || !options.network))
13784 #endif
13785   {
13786     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13787     int offset = game.scroll_delay_value;
13788
13789     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13790     {
13791       /* actual player has left the screen -- scroll in that direction */
13792       if (jx != old_jx)         /* player has moved horizontally */
13793         scroll_x += (jx - old_jx);
13794       else                      /* player has moved vertically */
13795         scroll_y += (jy - old_jy);
13796     }
13797     else
13798     {
13799       if (jx != old_jx)         /* player has moved horizontally */
13800       {
13801         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13802             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13803           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13804
13805         /* don't scroll over playfield boundaries */
13806         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13807           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13808
13809         /* don't scroll more than one field at a time */
13810         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13811
13812         /* don't scroll against the player's moving direction */
13813         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13814             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13815           scroll_x = old_scroll_x;
13816       }
13817       else                      /* player has moved vertically */
13818       {
13819         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13820             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13821           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13822
13823         /* don't scroll over playfield boundaries */
13824         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13825           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13826
13827         /* don't scroll more than one field at a time */
13828         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13829
13830         /* don't scroll against the player's moving direction */
13831         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13832             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13833           scroll_y = old_scroll_y;
13834       }
13835     }
13836
13837     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13838     {
13839 #if 1
13840       if (!options.network && game.centered_player_nr == -1 &&
13841           !AllPlayersInVisibleScreen())
13842       {
13843         scroll_x = old_scroll_x;
13844         scroll_y = old_scroll_y;
13845       }
13846       else
13847 #else
13848       if (!options.network && !AllPlayersInVisibleScreen())
13849       {
13850         scroll_x = old_scroll_x;
13851         scroll_y = old_scroll_y;
13852       }
13853       else
13854 #endif
13855       {
13856         ScrollScreen(player, SCROLL_INIT);
13857         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13858       }
13859     }
13860   }
13861
13862   player->StepFrame = 0;
13863
13864   if (moved & MP_MOVING)
13865   {
13866     if (old_jx != jx && old_jy == jy)
13867       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13868     else if (old_jx == jx && old_jy != jy)
13869       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13870
13871     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13872
13873     player->last_move_dir = player->MovDir;
13874     player->is_moving = TRUE;
13875     player->is_snapping = FALSE;
13876     player->is_switching = FALSE;
13877     player->is_dropping = FALSE;
13878     player->is_dropping_pressed = FALSE;
13879     player->drop_pressed_delay = 0;
13880
13881 #if 0
13882     /* should better be called here than above, but this breaks some tapes */
13883     ScrollPlayer(player, SCROLL_INIT);
13884 #endif
13885   }
13886   else
13887   {
13888     CheckGravityMovementWhenNotMoving(player);
13889
13890     player->is_moving = FALSE;
13891
13892     /* at this point, the player is allowed to move, but cannot move right now
13893        (e.g. because of something blocking the way) -- ensure that the player
13894        is also allowed to move in the next frame (in old versions before 3.1.1,
13895        the player was forced to wait again for eight frames before next try) */
13896
13897     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13898       player->move_delay = 0;   /* allow direct movement in the next frame */
13899   }
13900
13901   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13902     player->move_delay = player->move_delay_value;
13903
13904   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13905   {
13906     TestIfPlayerTouchesBadThing(jx, jy);
13907     TestIfPlayerTouchesCustomElement(jx, jy);
13908   }
13909
13910   if (!player->active)
13911     RemovePlayer(player);
13912
13913   return moved;
13914 }
13915
13916 void ScrollPlayer(struct PlayerInfo *player, int mode)
13917 {
13918   int jx = player->jx, jy = player->jy;
13919   int last_jx = player->last_jx, last_jy = player->last_jy;
13920   int move_stepsize = TILEX / player->move_delay_value;
13921
13922 #if USE_NEW_PLAYER_SPEED
13923   if (!player->active)
13924     return;
13925
13926   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13927     return;
13928 #else
13929   if (!player->active || player->MovPos == 0)
13930     return;
13931 #endif
13932
13933   if (mode == SCROLL_INIT)
13934   {
13935     player->actual_frame_counter = FrameCounter;
13936     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13937
13938     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13939         Feld[last_jx][last_jy] == EL_EMPTY)
13940     {
13941       int last_field_block_delay = 0;   /* start with no blocking at all */
13942       int block_delay_adjustment = player->block_delay_adjustment;
13943
13944       /* if player blocks last field, add delay for exactly one move */
13945       if (player->block_last_field)
13946       {
13947         last_field_block_delay += player->move_delay_value;
13948
13949         /* when blocking enabled, prevent moving up despite gravity */
13950 #if USE_PLAYER_GRAVITY
13951         if (player->gravity && player->MovDir == MV_UP)
13952           block_delay_adjustment = -1;
13953 #else
13954         if (game.gravity && player->MovDir == MV_UP)
13955           block_delay_adjustment = -1;
13956 #endif
13957       }
13958
13959       /* add block delay adjustment (also possible when not blocking) */
13960       last_field_block_delay += block_delay_adjustment;
13961
13962       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13963       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13964     }
13965
13966 #if USE_NEW_PLAYER_SPEED
13967     if (player->MovPos != 0)    /* player has not yet reached destination */
13968       return;
13969 #else
13970     return;
13971 #endif
13972   }
13973   else if (!FrameReached(&player->actual_frame_counter, 1))
13974     return;
13975
13976 #if USE_NEW_PLAYER_SPEED
13977   if (player->MovPos != 0)
13978   {
13979     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13980     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13981
13982     /* before DrawPlayer() to draw correct player graphic for this case */
13983     if (player->MovPos == 0)
13984       CheckGravityMovement(player);
13985   }
13986 #else
13987   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13988   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13989
13990   /* before DrawPlayer() to draw correct player graphic for this case */
13991   if (player->MovPos == 0)
13992     CheckGravityMovement(player);
13993 #endif
13994
13995   if (player->MovPos == 0)      /* player reached destination field */
13996   {
13997     if (player->move_delay_reset_counter > 0)
13998     {
13999       player->move_delay_reset_counter--;
14000
14001       if (player->move_delay_reset_counter == 0)
14002       {
14003         /* continue with normal speed after quickly moving through gate */
14004         HALVE_PLAYER_SPEED(player);
14005
14006         /* be able to make the next move without delay */
14007         player->move_delay = 0;
14008       }
14009     }
14010
14011     player->last_jx = jx;
14012     player->last_jy = jy;
14013
14014     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14015         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14016 #if 1
14017         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14018 #endif
14019         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14020         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14021 #if 1
14022         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14023 #endif
14024         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14025         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14026     {
14027       DrawPlayer(player);       /* needed here only to cleanup last field */
14028       RemovePlayer(player);
14029
14030       if (local_player->friends_still_needed == 0 ||
14031           IS_SP_ELEMENT(Feld[jx][jy]))
14032         PlayerWins(player);
14033     }
14034
14035     /* this breaks one level: "machine", level 000 */
14036     {
14037       int move_direction = player->MovDir;
14038       int enter_side = MV_DIR_OPPOSITE(move_direction);
14039       int leave_side = move_direction;
14040       int old_jx = last_jx;
14041       int old_jy = last_jy;
14042       int old_element = Feld[old_jx][old_jy];
14043       int new_element = Feld[jx][jy];
14044
14045       if (IS_CUSTOM_ELEMENT(old_element))
14046         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14047                                    CE_LEFT_BY_PLAYER,
14048                                    player->index_bit, leave_side);
14049
14050       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14051                                           CE_PLAYER_LEAVES_X,
14052                                           player->index_bit, leave_side);
14053
14054       if (IS_CUSTOM_ELEMENT(new_element))
14055         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14056                                    player->index_bit, enter_side);
14057
14058       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14059                                           CE_PLAYER_ENTERS_X,
14060                                           player->index_bit, enter_side);
14061
14062 #if USE_FIX_CE_ACTION_WITH_PLAYER
14063       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14064                                         CE_MOVE_OF_X, move_direction);
14065 #else
14066       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14067                                         CE_MOVE_OF_X, move_direction);
14068 #endif
14069     }
14070
14071     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14072     {
14073       TestIfPlayerTouchesBadThing(jx, jy);
14074       TestIfPlayerTouchesCustomElement(jx, jy);
14075
14076       /* needed because pushed element has not yet reached its destination,
14077          so it would trigger a change event at its previous field location */
14078       if (!player->is_pushing)
14079         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14080
14081       if (!player->active)
14082         RemovePlayer(player);
14083     }
14084
14085     if (!local_player->LevelSolved && level.use_step_counter)
14086     {
14087       int i;
14088
14089       TimePlayed++;
14090
14091       if (TimeLeft > 0)
14092       {
14093         TimeLeft--;
14094
14095         if (TimeLeft <= 10 && setup.time_limit)
14096           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14097
14098 #if 1
14099         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14100
14101         DisplayGameControlValues();
14102 #else
14103         DrawGameValue_Time(TimeLeft);
14104 #endif
14105
14106         if (!TimeLeft && setup.time_limit)
14107           for (i = 0; i < MAX_PLAYERS; i++)
14108             KillPlayer(&stored_player[i]);
14109       }
14110 #if 1
14111       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14112       {
14113         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14114
14115         DisplayGameControlValues();
14116       }
14117 #else
14118       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14119         DrawGameValue_Time(TimePlayed);
14120 #endif
14121     }
14122
14123     if (tape.single_step && tape.recording && !tape.pausing &&
14124         !player->programmed_action)
14125       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14126   }
14127 }
14128
14129 void ScrollScreen(struct PlayerInfo *player, int mode)
14130 {
14131   static unsigned int screen_frame_counter = 0;
14132
14133   if (mode == SCROLL_INIT)
14134   {
14135     /* set scrolling step size according to actual player's moving speed */
14136     ScrollStepSize = TILEX / player->move_delay_value;
14137
14138     screen_frame_counter = FrameCounter;
14139     ScreenMovDir = player->MovDir;
14140     ScreenMovPos = player->MovPos;
14141     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14142     return;
14143   }
14144   else if (!FrameReached(&screen_frame_counter, 1))
14145     return;
14146
14147   if (ScreenMovPos)
14148   {
14149     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14150     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14151     redraw_mask |= REDRAW_FIELD;
14152   }
14153   else
14154     ScreenMovDir = MV_NONE;
14155 }
14156
14157 void TestIfPlayerTouchesCustomElement(int x, int y)
14158 {
14159   static int xy[4][2] =
14160   {
14161     { 0, -1 },
14162     { -1, 0 },
14163     { +1, 0 },
14164     { 0, +1 }
14165   };
14166   static int trigger_sides[4][2] =
14167   {
14168     /* center side       border side */
14169     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14170     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14171     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14172     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14173   };
14174   static int touch_dir[4] =
14175   {
14176     MV_LEFT | MV_RIGHT,
14177     MV_UP   | MV_DOWN,
14178     MV_UP   | MV_DOWN,
14179     MV_LEFT | MV_RIGHT
14180   };
14181   int center_element = Feld[x][y];      /* should always be non-moving! */
14182   int i;
14183
14184   for (i = 0; i < NUM_DIRECTIONS; i++)
14185   {
14186     int xx = x + xy[i][0];
14187     int yy = y + xy[i][1];
14188     int center_side = trigger_sides[i][0];
14189     int border_side = trigger_sides[i][1];
14190     int border_element;
14191
14192     if (!IN_LEV_FIELD(xx, yy))
14193       continue;
14194
14195     if (IS_PLAYER(x, y))                /* player found at center element */
14196     {
14197       struct PlayerInfo *player = PLAYERINFO(x, y);
14198
14199       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14200         border_element = Feld[xx][yy];          /* may be moving! */
14201       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14202         border_element = Feld[xx][yy];
14203       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14204         border_element = MovingOrBlocked2Element(xx, yy);
14205       else
14206         continue;               /* center and border element do not touch */
14207
14208       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14209                                  player->index_bit, border_side);
14210       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14211                                           CE_PLAYER_TOUCHES_X,
14212                                           player->index_bit, border_side);
14213
14214 #if USE_FIX_CE_ACTION_WITH_PLAYER
14215       {
14216         /* use player element that is initially defined in the level playfield,
14217            not the player element that corresponds to the runtime player number
14218            (example: a level that contains EL_PLAYER_3 as the only player would
14219            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14220         int player_element = PLAYERINFO(x, y)->initial_element;
14221
14222         CheckElementChangeBySide(xx, yy, border_element, player_element,
14223                                  CE_TOUCHING_X, border_side);
14224       }
14225 #endif
14226     }
14227     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14228     {
14229       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14230
14231       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14232       {
14233         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14234           continue;             /* center and border element do not touch */
14235       }
14236
14237       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14238                                  player->index_bit, center_side);
14239       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14240                                           CE_PLAYER_TOUCHES_X,
14241                                           player->index_bit, center_side);
14242
14243 #if USE_FIX_CE_ACTION_WITH_PLAYER
14244       {
14245         /* use player element that is initially defined in the level playfield,
14246            not the player element that corresponds to the runtime player number
14247            (example: a level that contains EL_PLAYER_3 as the only player would
14248            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14249         int player_element = PLAYERINFO(xx, yy)->initial_element;
14250
14251         CheckElementChangeBySide(x, y, center_element, player_element,
14252                                  CE_TOUCHING_X, center_side);
14253       }
14254 #endif
14255
14256       break;
14257     }
14258   }
14259 }
14260
14261 #if USE_ELEMENT_TOUCHING_BUGFIX
14262
14263 void TestIfElementTouchesCustomElement(int x, int y)
14264 {
14265   static int xy[4][2] =
14266   {
14267     { 0, -1 },
14268     { -1, 0 },
14269     { +1, 0 },
14270     { 0, +1 }
14271   };
14272   static int trigger_sides[4][2] =
14273   {
14274     /* center side      border side */
14275     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14276     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14277     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14278     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14279   };
14280   static int touch_dir[4] =
14281   {
14282     MV_LEFT | MV_RIGHT,
14283     MV_UP   | MV_DOWN,
14284     MV_UP   | MV_DOWN,
14285     MV_LEFT | MV_RIGHT
14286   };
14287   boolean change_center_element = FALSE;
14288   int center_element = Feld[x][y];      /* should always be non-moving! */
14289   int border_element_old[NUM_DIRECTIONS];
14290   int i;
14291
14292   for (i = 0; i < NUM_DIRECTIONS; i++)
14293   {
14294     int xx = x + xy[i][0];
14295     int yy = y + xy[i][1];
14296     int border_element;
14297
14298     border_element_old[i] = -1;
14299
14300     if (!IN_LEV_FIELD(xx, yy))
14301       continue;
14302
14303     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14304       border_element = Feld[xx][yy];    /* may be moving! */
14305     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14306       border_element = Feld[xx][yy];
14307     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14308       border_element = MovingOrBlocked2Element(xx, yy);
14309     else
14310       continue;                 /* center and border element do not touch */
14311
14312     border_element_old[i] = border_element;
14313   }
14314
14315   for (i = 0; i < NUM_DIRECTIONS; i++)
14316   {
14317     int xx = x + xy[i][0];
14318     int yy = y + xy[i][1];
14319     int center_side = trigger_sides[i][0];
14320     int border_element = border_element_old[i];
14321
14322     if (border_element == -1)
14323       continue;
14324
14325     /* check for change of border element */
14326     CheckElementChangeBySide(xx, yy, border_element, center_element,
14327                              CE_TOUCHING_X, center_side);
14328
14329     /* (center element cannot be player, so we dont have to check this here) */
14330   }
14331
14332   for (i = 0; i < NUM_DIRECTIONS; i++)
14333   {
14334     int xx = x + xy[i][0];
14335     int yy = y + xy[i][1];
14336     int border_side = trigger_sides[i][1];
14337     int border_element = border_element_old[i];
14338
14339     if (border_element == -1)
14340       continue;
14341
14342     /* check for change of center element (but change it only once) */
14343     if (!change_center_element)
14344       change_center_element =
14345         CheckElementChangeBySide(x, y, center_element, border_element,
14346                                  CE_TOUCHING_X, border_side);
14347
14348 #if USE_FIX_CE_ACTION_WITH_PLAYER
14349     if (IS_PLAYER(xx, yy))
14350     {
14351       /* use player element that is initially defined in the level playfield,
14352          not the player element that corresponds to the runtime player number
14353          (example: a level that contains EL_PLAYER_3 as the only player would
14354          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14355       int player_element = PLAYERINFO(xx, yy)->initial_element;
14356
14357       CheckElementChangeBySide(x, y, center_element, player_element,
14358                                CE_TOUCHING_X, border_side);
14359     }
14360 #endif
14361   }
14362 }
14363
14364 #else
14365
14366 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14367 {
14368   static int xy[4][2] =
14369   {
14370     { 0, -1 },
14371     { -1, 0 },
14372     { +1, 0 },
14373     { 0, +1 }
14374   };
14375   static int trigger_sides[4][2] =
14376   {
14377     /* center side      border side */
14378     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14379     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14380     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14381     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14382   };
14383   static int touch_dir[4] =
14384   {
14385     MV_LEFT | MV_RIGHT,
14386     MV_UP   | MV_DOWN,
14387     MV_UP   | MV_DOWN,
14388     MV_LEFT | MV_RIGHT
14389   };
14390   boolean change_center_element = FALSE;
14391   int center_element = Feld[x][y];      /* should always be non-moving! */
14392   int i;
14393
14394   for (i = 0; i < NUM_DIRECTIONS; i++)
14395   {
14396     int xx = x + xy[i][0];
14397     int yy = y + xy[i][1];
14398     int center_side = trigger_sides[i][0];
14399     int border_side = trigger_sides[i][1];
14400     int border_element;
14401
14402     if (!IN_LEV_FIELD(xx, yy))
14403       continue;
14404
14405     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14406       border_element = Feld[xx][yy];    /* may be moving! */
14407     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14408       border_element = Feld[xx][yy];
14409     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14410       border_element = MovingOrBlocked2Element(xx, yy);
14411     else
14412       continue;                 /* center and border element do not touch */
14413
14414     /* check for change of center element (but change it only once) */
14415     if (!change_center_element)
14416       change_center_element =
14417         CheckElementChangeBySide(x, y, center_element, border_element,
14418                                  CE_TOUCHING_X, border_side);
14419
14420     /* check for change of border element */
14421     CheckElementChangeBySide(xx, yy, border_element, center_element,
14422                              CE_TOUCHING_X, center_side);
14423   }
14424 }
14425
14426 #endif
14427
14428 void TestIfElementHitsCustomElement(int x, int y, int direction)
14429 {
14430   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14431   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14432   int hitx = x + dx, hity = y + dy;
14433   int hitting_element = Feld[x][y];
14434   int touched_element;
14435
14436   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14437     return;
14438
14439   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14440                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14441
14442   if (IN_LEV_FIELD(hitx, hity))
14443   {
14444     int opposite_direction = MV_DIR_OPPOSITE(direction);
14445     int hitting_side = direction;
14446     int touched_side = opposite_direction;
14447     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14448                           MovDir[hitx][hity] != direction ||
14449                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14450
14451     object_hit = TRUE;
14452
14453     if (object_hit)
14454     {
14455       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14456                                CE_HITTING_X, touched_side);
14457
14458       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14459                                CE_HIT_BY_X, hitting_side);
14460
14461       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14462                                CE_HIT_BY_SOMETHING, opposite_direction);
14463
14464 #if USE_FIX_CE_ACTION_WITH_PLAYER
14465       if (IS_PLAYER(hitx, hity))
14466       {
14467         /* use player element that is initially defined in the level playfield,
14468            not the player element that corresponds to the runtime player number
14469            (example: a level that contains EL_PLAYER_3 as the only player would
14470            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14471         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14472
14473         CheckElementChangeBySide(x, y, hitting_element, player_element,
14474                                  CE_HITTING_X, touched_side);
14475       }
14476 #endif
14477     }
14478   }
14479
14480   /* "hitting something" is also true when hitting the playfield border */
14481   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14482                            CE_HITTING_SOMETHING, direction);
14483 }
14484
14485 #if 0
14486 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14487 {
14488   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14489   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14490   int hitx = x + dx, hity = y + dy;
14491   int hitting_element = Feld[x][y];
14492   int touched_element;
14493 #if 0
14494   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14495                         !IS_FREE(hitx, hity) &&
14496                         (!IS_MOVING(hitx, hity) ||
14497                          MovDir[hitx][hity] != direction ||
14498                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14499 #endif
14500
14501   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14502     return;
14503
14504 #if 0
14505   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14506     return;
14507 #endif
14508
14509   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14510                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14511
14512   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14513                            EP_CAN_SMASH_EVERYTHING, direction);
14514
14515   if (IN_LEV_FIELD(hitx, hity))
14516   {
14517     int opposite_direction = MV_DIR_OPPOSITE(direction);
14518     int hitting_side = direction;
14519     int touched_side = opposite_direction;
14520 #if 0
14521     int touched_element = MovingOrBlocked2Element(hitx, hity);
14522 #endif
14523 #if 1
14524     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14525                           MovDir[hitx][hity] != direction ||
14526                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14527
14528     object_hit = TRUE;
14529 #endif
14530
14531     if (object_hit)
14532     {
14533       int i;
14534
14535       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14536                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14537
14538       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14539                                CE_OTHER_IS_SMASHING, touched_side);
14540
14541       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14542                                CE_OTHER_GETS_SMASHED, hitting_side);
14543     }
14544   }
14545 }
14546 #endif
14547
14548 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14549 {
14550   int i, kill_x = -1, kill_y = -1;
14551
14552   int bad_element = -1;
14553   static int test_xy[4][2] =
14554   {
14555     { 0, -1 },
14556     { -1, 0 },
14557     { +1, 0 },
14558     { 0, +1 }
14559   };
14560   static int test_dir[4] =
14561   {
14562     MV_UP,
14563     MV_LEFT,
14564     MV_RIGHT,
14565     MV_DOWN
14566   };
14567
14568   for (i = 0; i < NUM_DIRECTIONS; i++)
14569   {
14570     int test_x, test_y, test_move_dir, test_element;
14571
14572     test_x = good_x + test_xy[i][0];
14573     test_y = good_y + test_xy[i][1];
14574
14575     if (!IN_LEV_FIELD(test_x, test_y))
14576       continue;
14577
14578     test_move_dir =
14579       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14580
14581     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14582
14583     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14584        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14585     */
14586     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14587         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14588     {
14589       kill_x = test_x;
14590       kill_y = test_y;
14591       bad_element = test_element;
14592
14593       break;
14594     }
14595   }
14596
14597   if (kill_x != -1 || kill_y != -1)
14598   {
14599     if (IS_PLAYER(good_x, good_y))
14600     {
14601       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14602
14603       if (player->shield_deadly_time_left > 0 &&
14604           !IS_INDESTRUCTIBLE(bad_element))
14605         Bang(kill_x, kill_y);
14606       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14607         KillPlayer(player);
14608     }
14609     else
14610       Bang(good_x, good_y);
14611   }
14612 }
14613
14614 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14615 {
14616   int i, kill_x = -1, kill_y = -1;
14617   int bad_element = Feld[bad_x][bad_y];
14618   static int test_xy[4][2] =
14619   {
14620     { 0, -1 },
14621     { -1, 0 },
14622     { +1, 0 },
14623     { 0, +1 }
14624   };
14625   static int touch_dir[4] =
14626   {
14627     MV_LEFT | MV_RIGHT,
14628     MV_UP   | MV_DOWN,
14629     MV_UP   | MV_DOWN,
14630     MV_LEFT | MV_RIGHT
14631   };
14632   static int test_dir[4] =
14633   {
14634     MV_UP,
14635     MV_LEFT,
14636     MV_RIGHT,
14637     MV_DOWN
14638   };
14639
14640   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14641     return;
14642
14643   for (i = 0; i < NUM_DIRECTIONS; i++)
14644   {
14645     int test_x, test_y, test_move_dir, test_element;
14646
14647     test_x = bad_x + test_xy[i][0];
14648     test_y = bad_y + test_xy[i][1];
14649
14650     if (!IN_LEV_FIELD(test_x, test_y))
14651       continue;
14652
14653     test_move_dir =
14654       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14655
14656     test_element = Feld[test_x][test_y];
14657
14658     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14659        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14660     */
14661     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14662         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14663     {
14664       /* good thing is player or penguin that does not move away */
14665       if (IS_PLAYER(test_x, test_y))
14666       {
14667         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14668
14669         if (bad_element == EL_ROBOT && player->is_moving)
14670           continue;     /* robot does not kill player if he is moving */
14671
14672         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14673         {
14674           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14675             continue;           /* center and border element do not touch */
14676         }
14677
14678         kill_x = test_x;
14679         kill_y = test_y;
14680
14681         break;
14682       }
14683       else if (test_element == EL_PENGUIN)
14684       {
14685         kill_x = test_x;
14686         kill_y = test_y;
14687
14688         break;
14689       }
14690     }
14691   }
14692
14693   if (kill_x != -1 || kill_y != -1)
14694   {
14695     if (IS_PLAYER(kill_x, kill_y))
14696     {
14697       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14698
14699       if (player->shield_deadly_time_left > 0 &&
14700           !IS_INDESTRUCTIBLE(bad_element))
14701         Bang(bad_x, bad_y);
14702       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14703         KillPlayer(player);
14704     }
14705     else
14706       Bang(kill_x, kill_y);
14707   }
14708 }
14709
14710 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14711 {
14712   int bad_element = Feld[bad_x][bad_y];
14713   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14714   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14715   int test_x = bad_x + dx, test_y = bad_y + dy;
14716   int test_move_dir, test_element;
14717   int kill_x = -1, kill_y = -1;
14718
14719   if (!IN_LEV_FIELD(test_x, test_y))
14720     return;
14721
14722   test_move_dir =
14723     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14724
14725   test_element = Feld[test_x][test_y];
14726
14727   if (test_move_dir != bad_move_dir)
14728   {
14729     /* good thing can be player or penguin that does not move away */
14730     if (IS_PLAYER(test_x, test_y))
14731     {
14732       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14733
14734       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14735          player as being hit when he is moving towards the bad thing, because
14736          the "get hit by" condition would be lost after the player stops) */
14737       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14738         return;         /* player moves away from bad thing */
14739
14740       kill_x = test_x;
14741       kill_y = test_y;
14742     }
14743     else if (test_element == EL_PENGUIN)
14744     {
14745       kill_x = test_x;
14746       kill_y = test_y;
14747     }
14748   }
14749
14750   if (kill_x != -1 || kill_y != -1)
14751   {
14752     if (IS_PLAYER(kill_x, kill_y))
14753     {
14754       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14755
14756       if (player->shield_deadly_time_left > 0 &&
14757           !IS_INDESTRUCTIBLE(bad_element))
14758         Bang(bad_x, bad_y);
14759       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14760         KillPlayer(player);
14761     }
14762     else
14763       Bang(kill_x, kill_y);
14764   }
14765 }
14766
14767 void TestIfPlayerTouchesBadThing(int x, int y)
14768 {
14769   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14770 }
14771
14772 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14773 {
14774   TestIfGoodThingHitsBadThing(x, y, move_dir);
14775 }
14776
14777 void TestIfBadThingTouchesPlayer(int x, int y)
14778 {
14779   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14780 }
14781
14782 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14783 {
14784   TestIfBadThingHitsGoodThing(x, y, move_dir);
14785 }
14786
14787 void TestIfFriendTouchesBadThing(int x, int y)
14788 {
14789   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14790 }
14791
14792 void TestIfBadThingTouchesFriend(int x, int y)
14793 {
14794   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14795 }
14796
14797 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14798 {
14799   int i, kill_x = bad_x, kill_y = bad_y;
14800   static int xy[4][2] =
14801   {
14802     { 0, -1 },
14803     { -1, 0 },
14804     { +1, 0 },
14805     { 0, +1 }
14806   };
14807
14808   for (i = 0; i < NUM_DIRECTIONS; i++)
14809   {
14810     int x, y, element;
14811
14812     x = bad_x + xy[i][0];
14813     y = bad_y + xy[i][1];
14814     if (!IN_LEV_FIELD(x, y))
14815       continue;
14816
14817     element = Feld[x][y];
14818     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14819         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14820     {
14821       kill_x = x;
14822       kill_y = y;
14823       break;
14824     }
14825   }
14826
14827   if (kill_x != bad_x || kill_y != bad_y)
14828     Bang(bad_x, bad_y);
14829 }
14830
14831 void KillPlayer(struct PlayerInfo *player)
14832 {
14833   int jx = player->jx, jy = player->jy;
14834
14835   if (!player->active)
14836     return;
14837
14838 #if 0
14839   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14840          player->killed, player->active, player->reanimated);
14841 #endif
14842
14843   /* the following code was introduced to prevent an infinite loop when calling
14844      -> Bang()
14845      -> CheckTriggeredElementChangeExt()
14846      -> ExecuteCustomElementAction()
14847      -> KillPlayer()
14848      -> (infinitely repeating the above sequence of function calls)
14849      which occurs when killing the player while having a CE with the setting
14850      "kill player X when explosion of <player X>"; the solution using a new
14851      field "player->killed" was chosen for backwards compatibility, although
14852      clever use of the fields "player->active" etc. would probably also work */
14853 #if 1
14854   if (player->killed)
14855     return;
14856 #endif
14857
14858   player->killed = TRUE;
14859
14860   /* remove accessible field at the player's position */
14861   Feld[jx][jy] = EL_EMPTY;
14862
14863   /* deactivate shield (else Bang()/Explode() would not work right) */
14864   player->shield_normal_time_left = 0;
14865   player->shield_deadly_time_left = 0;
14866
14867 #if 0
14868   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14869          player->killed, player->active, player->reanimated);
14870 #endif
14871
14872   Bang(jx, jy);
14873
14874 #if 0
14875   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14876          player->killed, player->active, player->reanimated);
14877 #endif
14878
14879 #if USE_PLAYER_REANIMATION
14880 #if 1
14881   if (player->reanimated)       /* killed player may have been reanimated */
14882     player->killed = player->reanimated = FALSE;
14883   else
14884     BuryPlayer(player);
14885 #else
14886   if (player->killed)           /* player may have been reanimated */
14887     BuryPlayer(player);
14888 #endif
14889 #else
14890   BuryPlayer(player);
14891 #endif
14892 }
14893
14894 static void KillPlayerUnlessEnemyProtected(int x, int y)
14895 {
14896   if (!PLAYER_ENEMY_PROTECTED(x, y))
14897     KillPlayer(PLAYERINFO(x, y));
14898 }
14899
14900 static void KillPlayerUnlessExplosionProtected(int x, int y)
14901 {
14902   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14903     KillPlayer(PLAYERINFO(x, y));
14904 }
14905
14906 void BuryPlayer(struct PlayerInfo *player)
14907 {
14908   int jx = player->jx, jy = player->jy;
14909
14910   if (!player->active)
14911     return;
14912
14913   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14914   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14915
14916   player->GameOver = TRUE;
14917   RemovePlayer(player);
14918 }
14919
14920 void RemovePlayer(struct PlayerInfo *player)
14921 {
14922   int jx = player->jx, jy = player->jy;
14923   int i, found = FALSE;
14924
14925   player->present = FALSE;
14926   player->active = FALSE;
14927
14928   if (!ExplodeField[jx][jy])
14929     StorePlayer[jx][jy] = 0;
14930
14931   if (player->is_moving)
14932     TEST_DrawLevelField(player->last_jx, player->last_jy);
14933
14934   for (i = 0; i < MAX_PLAYERS; i++)
14935     if (stored_player[i].active)
14936       found = TRUE;
14937
14938   if (!found)
14939     AllPlayersGone = TRUE;
14940
14941   ExitX = ZX = jx;
14942   ExitY = ZY = jy;
14943 }
14944
14945 #if USE_NEW_SNAP_DELAY
14946 static void setFieldForSnapping(int x, int y, int element, int direction)
14947 {
14948   struct ElementInfo *ei = &element_info[element];
14949   int direction_bit = MV_DIR_TO_BIT(direction);
14950   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14951   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14952                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14953
14954   Feld[x][y] = EL_ELEMENT_SNAPPING;
14955   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14956
14957   ResetGfxAnimation(x, y);
14958
14959   GfxElement[x][y] = element;
14960   GfxAction[x][y] = action;
14961   GfxDir[x][y] = direction;
14962   GfxFrame[x][y] = -1;
14963 }
14964 #endif
14965
14966 /*
14967   =============================================================================
14968   checkDiagonalPushing()
14969   -----------------------------------------------------------------------------
14970   check if diagonal input device direction results in pushing of object
14971   (by checking if the alternative direction is walkable, diggable, ...)
14972   =============================================================================
14973 */
14974
14975 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14976                                     int x, int y, int real_dx, int real_dy)
14977 {
14978   int jx, jy, dx, dy, xx, yy;
14979
14980   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14981     return TRUE;
14982
14983   /* diagonal direction: check alternative direction */
14984   jx = player->jx;
14985   jy = player->jy;
14986   dx = x - jx;
14987   dy = y - jy;
14988   xx = jx + (dx == 0 ? real_dx : 0);
14989   yy = jy + (dy == 0 ? real_dy : 0);
14990
14991   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14992 }
14993
14994 /*
14995   =============================================================================
14996   DigField()
14997   -----------------------------------------------------------------------------
14998   x, y:                 field next to player (non-diagonal) to try to dig to
14999   real_dx, real_dy:     direction as read from input device (can be diagonal)
15000   =============================================================================
15001 */
15002
15003 static int DigField(struct PlayerInfo *player,
15004                     int oldx, int oldy, int x, int y,
15005                     int real_dx, int real_dy, int mode)
15006 {
15007   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15008   boolean player_was_pushing = player->is_pushing;
15009   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15010   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15011   int jx = oldx, jy = oldy;
15012   int dx = x - jx, dy = y - jy;
15013   int nextx = x + dx, nexty = y + dy;
15014   int move_direction = (dx == -1 ? MV_LEFT  :
15015                         dx == +1 ? MV_RIGHT :
15016                         dy == -1 ? MV_UP    :
15017                         dy == +1 ? MV_DOWN  : MV_NONE);
15018   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15019   int dig_side = MV_DIR_OPPOSITE(move_direction);
15020   int old_element = Feld[jx][jy];
15021 #if USE_FIXED_DONT_RUN_INTO
15022   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15023 #else
15024   int element;
15025 #endif
15026   int collect_count;
15027
15028   if (is_player)                /* function can also be called by EL_PENGUIN */
15029   {
15030     if (player->MovPos == 0)
15031     {
15032       player->is_digging = FALSE;
15033       player->is_collecting = FALSE;
15034     }
15035
15036     if (player->MovPos == 0)    /* last pushing move finished */
15037       player->is_pushing = FALSE;
15038
15039     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15040     {
15041       player->is_switching = FALSE;
15042       player->push_delay = -1;
15043
15044       return MP_NO_ACTION;
15045     }
15046   }
15047
15048 #if !USE_FIXED_DONT_RUN_INTO
15049   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15050     return MP_NO_ACTION;
15051 #endif
15052
15053   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15054     old_element = Back[jx][jy];
15055
15056   /* in case of element dropped at player position, check background */
15057   else if (Back[jx][jy] != EL_EMPTY &&
15058            game.engine_version >= VERSION_IDENT(2,2,0,0))
15059     old_element = Back[jx][jy];
15060
15061   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15062     return MP_NO_ACTION;        /* field has no opening in this direction */
15063
15064   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15065     return MP_NO_ACTION;        /* field has no opening in this direction */
15066
15067 #if USE_FIXED_DONT_RUN_INTO
15068   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15069   {
15070     SplashAcid(x, y);
15071
15072     Feld[jx][jy] = player->artwork_element;
15073     InitMovingField(jx, jy, MV_DOWN);
15074     Store[jx][jy] = EL_ACID;
15075     ContinueMoving(jx, jy);
15076     BuryPlayer(player);
15077
15078     return MP_DONT_RUN_INTO;
15079   }
15080 #endif
15081
15082 #if USE_FIXED_DONT_RUN_INTO
15083   if (player_can_move && DONT_RUN_INTO(element))
15084   {
15085     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15086
15087     return MP_DONT_RUN_INTO;
15088   }
15089 #endif
15090
15091 #if USE_FIXED_DONT_RUN_INTO
15092   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15093     return MP_NO_ACTION;
15094 #endif
15095
15096 #if !USE_FIXED_DONT_RUN_INTO
15097   element = Feld[x][y];
15098 #endif
15099
15100   collect_count = element_info[element].collect_count_initial;
15101
15102   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15103     return MP_NO_ACTION;
15104
15105   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15106     player_can_move = player_can_move_or_snap;
15107
15108   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15109       game.engine_version >= VERSION_IDENT(2,2,0,0))
15110   {
15111     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15112                                player->index_bit, dig_side);
15113     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15114                                         player->index_bit, dig_side);
15115
15116     if (element == EL_DC_LANDMINE)
15117       Bang(x, y);
15118
15119     if (Feld[x][y] != element)          /* field changed by snapping */
15120       return MP_ACTION;
15121
15122     return MP_NO_ACTION;
15123   }
15124
15125 #if USE_PLAYER_GRAVITY
15126   if (player->gravity && is_player && !player->is_auto_moving &&
15127       canFallDown(player) && move_direction != MV_DOWN &&
15128       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15129     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15130 #else
15131   if (game.gravity && is_player && !player->is_auto_moving &&
15132       canFallDown(player) && move_direction != MV_DOWN &&
15133       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15134     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15135 #endif
15136
15137   if (player_can_move &&
15138       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15139   {
15140     int sound_element = SND_ELEMENT(element);
15141     int sound_action = ACTION_WALKING;
15142
15143     if (IS_RND_GATE(element))
15144     {
15145       if (!player->key[RND_GATE_NR(element)])
15146         return MP_NO_ACTION;
15147     }
15148     else if (IS_RND_GATE_GRAY(element))
15149     {
15150       if (!player->key[RND_GATE_GRAY_NR(element)])
15151         return MP_NO_ACTION;
15152     }
15153     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15154     {
15155       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15156         return MP_NO_ACTION;
15157     }
15158     else if (element == EL_EXIT_OPEN ||
15159              element == EL_EM_EXIT_OPEN ||
15160 #if 1
15161              element == EL_EM_EXIT_OPENING ||
15162 #endif
15163              element == EL_STEEL_EXIT_OPEN ||
15164              element == EL_EM_STEEL_EXIT_OPEN ||
15165 #if 1
15166              element == EL_EM_STEEL_EXIT_OPENING ||
15167 #endif
15168              element == EL_SP_EXIT_OPEN ||
15169              element == EL_SP_EXIT_OPENING)
15170     {
15171       sound_action = ACTION_PASSING;    /* player is passing exit */
15172     }
15173     else if (element == EL_EMPTY)
15174     {
15175       sound_action = ACTION_MOVING;             /* nothing to walk on */
15176     }
15177
15178     /* play sound from background or player, whatever is available */
15179     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15180       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15181     else
15182       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15183   }
15184   else if (player_can_move &&
15185            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15186   {
15187     if (!ACCESS_FROM(element, opposite_direction))
15188       return MP_NO_ACTION;      /* field not accessible from this direction */
15189
15190     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15191       return MP_NO_ACTION;
15192
15193     if (IS_EM_GATE(element))
15194     {
15195       if (!player->key[EM_GATE_NR(element)])
15196         return MP_NO_ACTION;
15197     }
15198     else if (IS_EM_GATE_GRAY(element))
15199     {
15200       if (!player->key[EM_GATE_GRAY_NR(element)])
15201         return MP_NO_ACTION;
15202     }
15203     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15204     {
15205       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15206         return MP_NO_ACTION;
15207     }
15208     else if (IS_EMC_GATE(element))
15209     {
15210       if (!player->key[EMC_GATE_NR(element)])
15211         return MP_NO_ACTION;
15212     }
15213     else if (IS_EMC_GATE_GRAY(element))
15214     {
15215       if (!player->key[EMC_GATE_GRAY_NR(element)])
15216         return MP_NO_ACTION;
15217     }
15218     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15219     {
15220       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15221         return MP_NO_ACTION;
15222     }
15223     else if (element == EL_DC_GATE_WHITE ||
15224              element == EL_DC_GATE_WHITE_GRAY ||
15225              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15226     {
15227       if (player->num_white_keys == 0)
15228         return MP_NO_ACTION;
15229
15230       player->num_white_keys--;
15231     }
15232     else if (IS_SP_PORT(element))
15233     {
15234       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15235           element == EL_SP_GRAVITY_PORT_RIGHT ||
15236           element == EL_SP_GRAVITY_PORT_UP ||
15237           element == EL_SP_GRAVITY_PORT_DOWN)
15238 #if USE_PLAYER_GRAVITY
15239         player->gravity = !player->gravity;
15240 #else
15241         game.gravity = !game.gravity;
15242 #endif
15243       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15244                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15245                element == EL_SP_GRAVITY_ON_PORT_UP ||
15246                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15247 #if USE_PLAYER_GRAVITY
15248         player->gravity = TRUE;
15249 #else
15250         game.gravity = TRUE;
15251 #endif
15252       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15253                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15254                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15255                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15256 #if USE_PLAYER_GRAVITY
15257         player->gravity = FALSE;
15258 #else
15259         game.gravity = FALSE;
15260 #endif
15261     }
15262
15263     /* automatically move to the next field with double speed */
15264     player->programmed_action = move_direction;
15265
15266     if (player->move_delay_reset_counter == 0)
15267     {
15268       player->move_delay_reset_counter = 2;     /* two double speed steps */
15269
15270       DOUBLE_PLAYER_SPEED(player);
15271     }
15272
15273     PlayLevelSoundAction(x, y, ACTION_PASSING);
15274   }
15275   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15276   {
15277     RemoveField(x, y);
15278
15279     if (mode != DF_SNAP)
15280     {
15281       GfxElement[x][y] = GFX_ELEMENT(element);
15282       player->is_digging = TRUE;
15283     }
15284
15285     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15286
15287     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15288                                         player->index_bit, dig_side);
15289
15290     if (mode == DF_SNAP)
15291     {
15292 #if USE_NEW_SNAP_DELAY
15293       if (level.block_snap_field)
15294         setFieldForSnapping(x, y, element, move_direction);
15295       else
15296         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15297 #else
15298       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15299 #endif
15300
15301       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15302                                           player->index_bit, dig_side);
15303     }
15304   }
15305   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15306   {
15307     RemoveField(x, y);
15308
15309     if (is_player && mode != DF_SNAP)
15310     {
15311       GfxElement[x][y] = element;
15312       player->is_collecting = TRUE;
15313     }
15314
15315     if (element == EL_SPEED_PILL)
15316     {
15317       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15318     }
15319     else if (element == EL_EXTRA_TIME && level.time > 0)
15320     {
15321       TimeLeft += level.extra_time;
15322
15323 #if 1
15324       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15325
15326       DisplayGameControlValues();
15327 #else
15328       DrawGameValue_Time(TimeLeft);
15329 #endif
15330     }
15331     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15332     {
15333       player->shield_normal_time_left += level.shield_normal_time;
15334       if (element == EL_SHIELD_DEADLY)
15335         player->shield_deadly_time_left += level.shield_deadly_time;
15336     }
15337     else if (element == EL_DYNAMITE ||
15338              element == EL_EM_DYNAMITE ||
15339              element == EL_SP_DISK_RED)
15340     {
15341       if (player->inventory_size < MAX_INVENTORY_SIZE)
15342         player->inventory_element[player->inventory_size++] = element;
15343
15344       DrawGameDoorValues();
15345     }
15346     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15347     {
15348       player->dynabomb_count++;
15349       player->dynabombs_left++;
15350     }
15351     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15352     {
15353       player->dynabomb_size++;
15354     }
15355     else if (element == EL_DYNABOMB_INCREASE_POWER)
15356     {
15357       player->dynabomb_xl = TRUE;
15358     }
15359     else if (IS_KEY(element))
15360     {
15361       player->key[KEY_NR(element)] = TRUE;
15362
15363       DrawGameDoorValues();
15364     }
15365     else if (element == EL_DC_KEY_WHITE)
15366     {
15367       player->num_white_keys++;
15368
15369       /* display white keys? */
15370       /* DrawGameDoorValues(); */
15371     }
15372     else if (IS_ENVELOPE(element))
15373     {
15374       player->show_envelope = element;
15375     }
15376     else if (element == EL_EMC_LENSES)
15377     {
15378       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15379
15380       RedrawAllInvisibleElementsForLenses();
15381     }
15382     else if (element == EL_EMC_MAGNIFIER)
15383     {
15384       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15385
15386       RedrawAllInvisibleElementsForMagnifier();
15387     }
15388     else if (IS_DROPPABLE(element) ||
15389              IS_THROWABLE(element))     /* can be collected and dropped */
15390     {
15391       int i;
15392
15393       if (collect_count == 0)
15394         player->inventory_infinite_element = element;
15395       else
15396         for (i = 0; i < collect_count; i++)
15397           if (player->inventory_size < MAX_INVENTORY_SIZE)
15398             player->inventory_element[player->inventory_size++] = element;
15399
15400       DrawGameDoorValues();
15401     }
15402     else if (collect_count > 0)
15403     {
15404       local_player->gems_still_needed -= collect_count;
15405       if (local_player->gems_still_needed < 0)
15406         local_player->gems_still_needed = 0;
15407
15408 #if 1
15409       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15410
15411       DisplayGameControlValues();
15412 #else
15413       DrawGameValue_Emeralds(local_player->gems_still_needed);
15414 #endif
15415     }
15416
15417     RaiseScoreElement(element);
15418     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15419
15420     if (is_player)
15421       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15422                                           player->index_bit, dig_side);
15423
15424     if (mode == DF_SNAP)
15425     {
15426 #if USE_NEW_SNAP_DELAY
15427       if (level.block_snap_field)
15428         setFieldForSnapping(x, y, element, move_direction);
15429       else
15430         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15431 #else
15432       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15433 #endif
15434
15435       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15436                                           player->index_bit, dig_side);
15437     }
15438   }
15439   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15440   {
15441     if (mode == DF_SNAP && element != EL_BD_ROCK)
15442       return MP_NO_ACTION;
15443
15444     if (CAN_FALL(element) && dy)
15445       return MP_NO_ACTION;
15446
15447     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15448         !(element == EL_SPRING && level.use_spring_bug))
15449       return MP_NO_ACTION;
15450
15451     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15452         ((move_direction & MV_VERTICAL &&
15453           ((element_info[element].move_pattern & MV_LEFT &&
15454             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15455            (element_info[element].move_pattern & MV_RIGHT &&
15456             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15457          (move_direction & MV_HORIZONTAL &&
15458           ((element_info[element].move_pattern & MV_UP &&
15459             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15460            (element_info[element].move_pattern & MV_DOWN &&
15461             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15462       return MP_NO_ACTION;
15463
15464     /* do not push elements already moving away faster than player */
15465     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15466         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15467       return MP_NO_ACTION;
15468
15469     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15470     {
15471       if (player->push_delay_value == -1 || !player_was_pushing)
15472         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15473     }
15474     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15475     {
15476       if (player->push_delay_value == -1)
15477         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15478     }
15479     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15480     {
15481       if (!player->is_pushing)
15482         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15483     }
15484
15485     player->is_pushing = TRUE;
15486     player->is_active = TRUE;
15487
15488     if (!(IN_LEV_FIELD(nextx, nexty) &&
15489           (IS_FREE(nextx, nexty) ||
15490            (IS_SB_ELEMENT(element) &&
15491             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15492            (IS_CUSTOM_ELEMENT(element) &&
15493             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15494       return MP_NO_ACTION;
15495
15496     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15497       return MP_NO_ACTION;
15498
15499     if (player->push_delay == -1)       /* new pushing; restart delay */
15500       player->push_delay = 0;
15501
15502     if (player->push_delay < player->push_delay_value &&
15503         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15504         element != EL_SPRING && element != EL_BALLOON)
15505     {
15506       /* make sure that there is no move delay before next try to push */
15507       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15508         player->move_delay = 0;
15509
15510       return MP_NO_ACTION;
15511     }
15512
15513     if (IS_CUSTOM_ELEMENT(element) &&
15514         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15515     {
15516       if (!DigFieldByCE(nextx, nexty, element))
15517         return MP_NO_ACTION;
15518     }
15519
15520     if (IS_SB_ELEMENT(element))
15521     {
15522       if (element == EL_SOKOBAN_FIELD_FULL)
15523       {
15524         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15525         local_player->sokobanfields_still_needed++;
15526       }
15527
15528       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15529       {
15530         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15531         local_player->sokobanfields_still_needed--;
15532       }
15533
15534       Feld[x][y] = EL_SOKOBAN_OBJECT;
15535
15536       if (Back[x][y] == Back[nextx][nexty])
15537         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15538       else if (Back[x][y] != 0)
15539         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15540                                     ACTION_EMPTYING);
15541       else
15542         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15543                                     ACTION_FILLING);
15544
15545 #if 1
15546       if (local_player->sokobanfields_still_needed == 0 &&
15547           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15548 #else
15549       if (local_player->sokobanfields_still_needed == 0 &&
15550           game.emulation == EMU_SOKOBAN)
15551 #endif
15552       {
15553         PlayerWins(player);
15554
15555         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15556       }
15557     }
15558     else
15559       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15560
15561     InitMovingField(x, y, move_direction);
15562     GfxAction[x][y] = ACTION_PUSHING;
15563
15564     if (mode == DF_SNAP)
15565       ContinueMoving(x, y);
15566     else
15567       MovPos[x][y] = (dx != 0 ? dx : dy);
15568
15569     Pushed[x][y] = TRUE;
15570     Pushed[nextx][nexty] = TRUE;
15571
15572     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15573       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15574     else
15575       player->push_delay_value = -1;    /* get new value later */
15576
15577     /* check for element change _after_ element has been pushed */
15578     if (game.use_change_when_pushing_bug)
15579     {
15580       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15581                                  player->index_bit, dig_side);
15582       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15583                                           player->index_bit, dig_side);
15584     }
15585   }
15586   else if (IS_SWITCHABLE(element))
15587   {
15588     if (PLAYER_SWITCHING(player, x, y))
15589     {
15590       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15591                                           player->index_bit, dig_side);
15592
15593       return MP_ACTION;
15594     }
15595
15596     player->is_switching = TRUE;
15597     player->switch_x = x;
15598     player->switch_y = y;
15599
15600     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15601
15602     if (element == EL_ROBOT_WHEEL)
15603     {
15604       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15605       ZX = x;
15606       ZY = y;
15607
15608       game.robot_wheel_active = TRUE;
15609
15610       TEST_DrawLevelField(x, y);
15611     }
15612     else if (element == EL_SP_TERMINAL)
15613     {
15614       int xx, yy;
15615
15616       SCAN_PLAYFIELD(xx, yy)
15617       {
15618         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15619           Bang(xx, yy);
15620         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15621           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15622       }
15623     }
15624     else if (IS_BELT_SWITCH(element))
15625     {
15626       ToggleBeltSwitch(x, y);
15627     }
15628     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15629              element == EL_SWITCHGATE_SWITCH_DOWN ||
15630              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15631              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15632     {
15633       ToggleSwitchgateSwitch(x, y);
15634     }
15635     else if (element == EL_LIGHT_SWITCH ||
15636              element == EL_LIGHT_SWITCH_ACTIVE)
15637     {
15638       ToggleLightSwitch(x, y);
15639     }
15640     else if (element == EL_TIMEGATE_SWITCH ||
15641              element == EL_DC_TIMEGATE_SWITCH)
15642     {
15643       ActivateTimegateSwitch(x, y);
15644     }
15645     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15646              element == EL_BALLOON_SWITCH_RIGHT ||
15647              element == EL_BALLOON_SWITCH_UP    ||
15648              element == EL_BALLOON_SWITCH_DOWN  ||
15649              element == EL_BALLOON_SWITCH_NONE  ||
15650              element == EL_BALLOON_SWITCH_ANY)
15651     {
15652       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15653                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15654                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15655                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15656                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15657                              move_direction);
15658     }
15659     else if (element == EL_LAMP)
15660     {
15661       Feld[x][y] = EL_LAMP_ACTIVE;
15662       local_player->lights_still_needed--;
15663
15664       ResetGfxAnimation(x, y);
15665       TEST_DrawLevelField(x, y);
15666     }
15667     else if (element == EL_TIME_ORB_FULL)
15668     {
15669       Feld[x][y] = EL_TIME_ORB_EMPTY;
15670
15671       if (level.time > 0 || level.use_time_orb_bug)
15672       {
15673         TimeLeft += level.time_orb_time;
15674         game.no_time_limit = FALSE;
15675
15676 #if 1
15677         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15678
15679         DisplayGameControlValues();
15680 #else
15681         DrawGameValue_Time(TimeLeft);
15682 #endif
15683       }
15684
15685       ResetGfxAnimation(x, y);
15686       TEST_DrawLevelField(x, y);
15687     }
15688     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15689              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15690     {
15691       int xx, yy;
15692
15693       game.ball_state = !game.ball_state;
15694
15695       SCAN_PLAYFIELD(xx, yy)
15696       {
15697         int e = Feld[xx][yy];
15698
15699         if (game.ball_state)
15700         {
15701           if (e == EL_EMC_MAGIC_BALL)
15702             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15703           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15704             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15705         }
15706         else
15707         {
15708           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15709             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15710           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15711             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15712         }
15713       }
15714     }
15715
15716     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15717                                         player->index_bit, dig_side);
15718
15719     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15720                                         player->index_bit, dig_side);
15721
15722     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15723                                         player->index_bit, dig_side);
15724
15725     return MP_ACTION;
15726   }
15727   else
15728   {
15729     if (!PLAYER_SWITCHING(player, x, y))
15730     {
15731       player->is_switching = TRUE;
15732       player->switch_x = x;
15733       player->switch_y = y;
15734
15735       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15736                                  player->index_bit, dig_side);
15737       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15738                                           player->index_bit, dig_side);
15739
15740       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15741                                  player->index_bit, dig_side);
15742       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15743                                           player->index_bit, dig_side);
15744     }
15745
15746     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15747                                player->index_bit, dig_side);
15748     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15749                                         player->index_bit, dig_side);
15750
15751     return MP_NO_ACTION;
15752   }
15753
15754   player->push_delay = -1;
15755
15756   if (is_player)                /* function can also be called by EL_PENGUIN */
15757   {
15758     if (Feld[x][y] != element)          /* really digged/collected something */
15759     {
15760       player->is_collecting = !player->is_digging;
15761       player->is_active = TRUE;
15762     }
15763   }
15764
15765   return MP_MOVING;
15766 }
15767
15768 static boolean DigFieldByCE(int x, int y, int digging_element)
15769 {
15770   int element = Feld[x][y];
15771
15772   if (!IS_FREE(x, y))
15773   {
15774     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15775                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15776                   ACTION_BREAKING);
15777
15778     /* no element can dig solid indestructible elements */
15779     if (IS_INDESTRUCTIBLE(element) &&
15780         !IS_DIGGABLE(element) &&
15781         !IS_COLLECTIBLE(element))
15782       return FALSE;
15783
15784     if (AmoebaNr[x][y] &&
15785         (element == EL_AMOEBA_FULL ||
15786          element == EL_BD_AMOEBA ||
15787          element == EL_AMOEBA_GROWING))
15788     {
15789       AmoebaCnt[AmoebaNr[x][y]]--;
15790       AmoebaCnt2[AmoebaNr[x][y]]--;
15791     }
15792
15793     if (IS_MOVING(x, y))
15794       RemoveMovingField(x, y);
15795     else
15796     {
15797       RemoveField(x, y);
15798       TEST_DrawLevelField(x, y);
15799     }
15800
15801     /* if digged element was about to explode, prevent the explosion */
15802     ExplodeField[x][y] = EX_TYPE_NONE;
15803
15804     PlayLevelSoundAction(x, y, action);
15805   }
15806
15807   Store[x][y] = EL_EMPTY;
15808
15809 #if 1
15810   /* this makes it possible to leave the removed element again */
15811   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15812     Store[x][y] = element;
15813 #else
15814   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15815   {
15816     int move_leave_element = element_info[digging_element].move_leave_element;
15817
15818     /* this makes it possible to leave the removed element again */
15819     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15820                    element : move_leave_element);
15821   }
15822 #endif
15823
15824   return TRUE;
15825 }
15826
15827 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15828 {
15829   int jx = player->jx, jy = player->jy;
15830   int x = jx + dx, y = jy + dy;
15831   int snap_direction = (dx == -1 ? MV_LEFT  :
15832                         dx == +1 ? MV_RIGHT :
15833                         dy == -1 ? MV_UP    :
15834                         dy == +1 ? MV_DOWN  : MV_NONE);
15835   boolean can_continue_snapping = (level.continuous_snapping &&
15836                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15837
15838   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15839     return FALSE;
15840
15841   if (!player->active || !IN_LEV_FIELD(x, y))
15842     return FALSE;
15843
15844   if (dx && dy)
15845     return FALSE;
15846
15847   if (!dx && !dy)
15848   {
15849     if (player->MovPos == 0)
15850       player->is_pushing = FALSE;
15851
15852     player->is_snapping = FALSE;
15853
15854     if (player->MovPos == 0)
15855     {
15856       player->is_moving = FALSE;
15857       player->is_digging = FALSE;
15858       player->is_collecting = FALSE;
15859     }
15860
15861     return FALSE;
15862   }
15863
15864 #if USE_NEW_CONTINUOUS_SNAPPING
15865   /* prevent snapping with already pressed snap key when not allowed */
15866   if (player->is_snapping && !can_continue_snapping)
15867     return FALSE;
15868 #else
15869   if (player->is_snapping)
15870     return FALSE;
15871 #endif
15872
15873   player->MovDir = snap_direction;
15874
15875   if (player->MovPos == 0)
15876   {
15877     player->is_moving = FALSE;
15878     player->is_digging = FALSE;
15879     player->is_collecting = FALSE;
15880   }
15881
15882   player->is_dropping = FALSE;
15883   player->is_dropping_pressed = FALSE;
15884   player->drop_pressed_delay = 0;
15885
15886   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15887     return FALSE;
15888
15889   player->is_snapping = TRUE;
15890   player->is_active = TRUE;
15891
15892   if (player->MovPos == 0)
15893   {
15894     player->is_moving = FALSE;
15895     player->is_digging = FALSE;
15896     player->is_collecting = FALSE;
15897   }
15898
15899   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15900     TEST_DrawLevelField(player->last_jx, player->last_jy);
15901
15902   TEST_DrawLevelField(x, y);
15903
15904   return TRUE;
15905 }
15906
15907 static boolean DropElement(struct PlayerInfo *player)
15908 {
15909   int old_element, new_element;
15910   int dropx = player->jx, dropy = player->jy;
15911   int drop_direction = player->MovDir;
15912   int drop_side = drop_direction;
15913 #if 1
15914   int drop_element = get_next_dropped_element(player);
15915 #else
15916   int drop_element = (player->inventory_size > 0 ?
15917                       player->inventory_element[player->inventory_size - 1] :
15918                       player->inventory_infinite_element != EL_UNDEFINED ?
15919                       player->inventory_infinite_element :
15920                       player->dynabombs_left > 0 ?
15921                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15922                       EL_UNDEFINED);
15923 #endif
15924
15925   player->is_dropping_pressed = TRUE;
15926
15927   /* do not drop an element on top of another element; when holding drop key
15928      pressed without moving, dropped element must move away before the next
15929      element can be dropped (this is especially important if the next element
15930      is dynamite, which can be placed on background for historical reasons) */
15931   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15932     return MP_ACTION;
15933
15934   if (IS_THROWABLE(drop_element))
15935   {
15936     dropx += GET_DX_FROM_DIR(drop_direction);
15937     dropy += GET_DY_FROM_DIR(drop_direction);
15938
15939     if (!IN_LEV_FIELD(dropx, dropy))
15940       return FALSE;
15941   }
15942
15943   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15944   new_element = drop_element;           /* default: no change when dropping */
15945
15946   /* check if player is active, not moving and ready to drop */
15947   if (!player->active || player->MovPos || player->drop_delay > 0)
15948     return FALSE;
15949
15950   /* check if player has anything that can be dropped */
15951   if (new_element == EL_UNDEFINED)
15952     return FALSE;
15953
15954   /* check if drop key was pressed long enough for EM style dynamite */
15955   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15956     return FALSE;
15957
15958   /* check if anything can be dropped at the current position */
15959   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15960     return FALSE;
15961
15962   /* collected custom elements can only be dropped on empty fields */
15963   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15964     return FALSE;
15965
15966   if (old_element != EL_EMPTY)
15967     Back[dropx][dropy] = old_element;   /* store old element on this field */
15968
15969   ResetGfxAnimation(dropx, dropy);
15970   ResetRandomAnimationValue(dropx, dropy);
15971
15972   if (player->inventory_size > 0 ||
15973       player->inventory_infinite_element != EL_UNDEFINED)
15974   {
15975     if (player->inventory_size > 0)
15976     {
15977       player->inventory_size--;
15978
15979       DrawGameDoorValues();
15980
15981       if (new_element == EL_DYNAMITE)
15982         new_element = EL_DYNAMITE_ACTIVE;
15983       else if (new_element == EL_EM_DYNAMITE)
15984         new_element = EL_EM_DYNAMITE_ACTIVE;
15985       else if (new_element == EL_SP_DISK_RED)
15986         new_element = EL_SP_DISK_RED_ACTIVE;
15987     }
15988
15989     Feld[dropx][dropy] = new_element;
15990
15991     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15992       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15993                           el2img(Feld[dropx][dropy]), 0);
15994
15995     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15996
15997     /* needed if previous element just changed to "empty" in the last frame */
15998     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15999
16000     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16001                                player->index_bit, drop_side);
16002     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16003                                         CE_PLAYER_DROPS_X,
16004                                         player->index_bit, drop_side);
16005
16006     TestIfElementTouchesCustomElement(dropx, dropy);
16007   }
16008   else          /* player is dropping a dyna bomb */
16009   {
16010     player->dynabombs_left--;
16011
16012     Feld[dropx][dropy] = new_element;
16013
16014     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16015       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16016                           el2img(Feld[dropx][dropy]), 0);
16017
16018     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16019   }
16020
16021   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16022     InitField_WithBug1(dropx, dropy, FALSE);
16023
16024   new_element = Feld[dropx][dropy];     /* element might have changed */
16025
16026   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16027       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16028   {
16029     int move_direction, nextx, nexty;
16030
16031     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16032       MovDir[dropx][dropy] = drop_direction;
16033
16034     move_direction = MovDir[dropx][dropy];
16035     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16036     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16037
16038     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16039
16040 #if USE_FIX_IMPACT_COLLISION
16041     /* do not cause impact style collision by dropping elements that can fall */
16042     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16043 #else
16044     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16045 #endif
16046   }
16047
16048   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16049   player->is_dropping = TRUE;
16050
16051   player->drop_pressed_delay = 0;
16052   player->is_dropping_pressed = FALSE;
16053
16054   player->drop_x = dropx;
16055   player->drop_y = dropy;
16056
16057   return TRUE;
16058 }
16059
16060 /* ------------------------------------------------------------------------- */
16061 /* game sound playing functions                                              */
16062 /* ------------------------------------------------------------------------- */
16063
16064 static int *loop_sound_frame = NULL;
16065 static int *loop_sound_volume = NULL;
16066
16067 void InitPlayLevelSound()
16068 {
16069   int num_sounds = getSoundListSize();
16070
16071   checked_free(loop_sound_frame);
16072   checked_free(loop_sound_volume);
16073
16074   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16075   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16076 }
16077
16078 static void PlayLevelSound(int x, int y, int nr)
16079 {
16080   int sx = SCREENX(x), sy = SCREENY(y);
16081   int volume, stereo_position;
16082   int max_distance = 8;
16083   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16084
16085   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16086       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16087     return;
16088
16089   if (!IN_LEV_FIELD(x, y) ||
16090       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16091       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16092     return;
16093
16094   volume = SOUND_MAX_VOLUME;
16095
16096   if (!IN_SCR_FIELD(sx, sy))
16097   {
16098     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16099     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16100
16101     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16102   }
16103
16104   stereo_position = (SOUND_MAX_LEFT +
16105                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16106                      (SCR_FIELDX + 2 * max_distance));
16107
16108   if (IS_LOOP_SOUND(nr))
16109   {
16110     /* This assures that quieter loop sounds do not overwrite louder ones,
16111        while restarting sound volume comparison with each new game frame. */
16112
16113     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16114       return;
16115
16116     loop_sound_volume[nr] = volume;
16117     loop_sound_frame[nr] = FrameCounter;
16118   }
16119
16120   PlaySoundExt(nr, volume, stereo_position, type);
16121 }
16122
16123 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16124 {
16125   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16126                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16127                  y < LEVELY(BY1) ? LEVELY(BY1) :
16128                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16129                  sound_action);
16130 }
16131
16132 static void PlayLevelSoundAction(int x, int y, int action)
16133 {
16134   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16135 }
16136
16137 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16138 {
16139   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16140
16141   if (sound_effect != SND_UNDEFINED)
16142     PlayLevelSound(x, y, sound_effect);
16143 }
16144
16145 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16146                                               int action)
16147 {
16148   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16149
16150   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16151     PlayLevelSound(x, y, sound_effect);
16152 }
16153
16154 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16155 {
16156   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16157
16158   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16159     PlayLevelSound(x, y, sound_effect);
16160 }
16161
16162 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16163 {
16164   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16165
16166   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16167     StopSound(sound_effect);
16168 }
16169
16170 static void PlayLevelMusic()
16171 {
16172   if (levelset.music[level_nr] != MUS_UNDEFINED)
16173     PlayMusic(levelset.music[level_nr]);        /* from config file */
16174   else
16175     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16176 }
16177
16178 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16179 {
16180   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16181   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16182   int x = xx - 1 - offset;
16183   int y = yy - 1 - offset;
16184
16185   switch (sample)
16186   {
16187     case SAMPLE_blank:
16188       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16189       break;
16190
16191     case SAMPLE_roll:
16192       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16193       break;
16194
16195     case SAMPLE_stone:
16196       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16197       break;
16198
16199     case SAMPLE_nut:
16200       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16201       break;
16202
16203     case SAMPLE_crack:
16204       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16205       break;
16206
16207     case SAMPLE_bug:
16208       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16209       break;
16210
16211     case SAMPLE_tank:
16212       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16213       break;
16214
16215     case SAMPLE_android_clone:
16216       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16217       break;
16218
16219     case SAMPLE_android_move:
16220       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16221       break;
16222
16223     case SAMPLE_spring:
16224       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16225       break;
16226
16227     case SAMPLE_slurp:
16228       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16229       break;
16230
16231     case SAMPLE_eater:
16232       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16233       break;
16234
16235     case SAMPLE_eater_eat:
16236       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16237       break;
16238
16239     case SAMPLE_alien:
16240       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16241       break;
16242
16243     case SAMPLE_collect:
16244       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16245       break;
16246
16247     case SAMPLE_diamond:
16248       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16249       break;
16250
16251     case SAMPLE_squash:
16252       /* !!! CHECK THIS !!! */
16253 #if 1
16254       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16255 #else
16256       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16257 #endif
16258       break;
16259
16260     case SAMPLE_wonderfall:
16261       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16262       break;
16263
16264     case SAMPLE_drip:
16265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16266       break;
16267
16268     case SAMPLE_push:
16269       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16270       break;
16271
16272     case SAMPLE_dirt:
16273       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16274       break;
16275
16276     case SAMPLE_acid:
16277       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16278       break;
16279
16280     case SAMPLE_ball:
16281       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16282       break;
16283
16284     case SAMPLE_grow:
16285       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16286       break;
16287
16288     case SAMPLE_wonder:
16289       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16290       break;
16291
16292     case SAMPLE_door:
16293       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16294       break;
16295
16296     case SAMPLE_exit_open:
16297       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16298       break;
16299
16300     case SAMPLE_exit_leave:
16301       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16302       break;
16303
16304     case SAMPLE_dynamite:
16305       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16306       break;
16307
16308     case SAMPLE_tick:
16309       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16310       break;
16311
16312     case SAMPLE_press:
16313       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16314       break;
16315
16316     case SAMPLE_wheel:
16317       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16318       break;
16319
16320     case SAMPLE_boom:
16321       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16322       break;
16323
16324     case SAMPLE_die:
16325       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16326       break;
16327
16328     case SAMPLE_time:
16329       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16330       break;
16331
16332     default:
16333       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16334       break;
16335   }
16336 }
16337
16338 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16339 {
16340   int element = map_element_SP_to_RND(element_sp);
16341   int action = map_action_SP_to_RND(action_sp);
16342   int offset = (setup.sp_show_border_elements ? 0 : 1);
16343   int x = xx - offset;
16344   int y = yy - offset;
16345
16346 #if 0
16347   printf("::: %d -> %d\n", element_sp, action_sp);
16348 #endif
16349
16350   PlayLevelSoundElementAction(x, y, element, action);
16351 }
16352
16353 #if 0
16354 void ChangeTime(int value)
16355 {
16356   int *time = (game.no_time_limit ? &TimePlayed : &TimeLeft);
16357
16358   *time += value;
16359
16360   /* EMC game engine uses value from time counter of RND game engine */
16361   level.native_em_level->lev->time = *time;
16362
16363   DrawGameValue_Time(*time);
16364 }
16365
16366 void RaiseScore(int value)
16367 {
16368   /* EMC game engine and RND game engine have separate score counters */
16369   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16370                 &level.native_em_level->lev->score : &local_player->score);
16371
16372   *score += value;
16373
16374   DrawGameValue_Score(*score);
16375 }
16376 #endif
16377
16378 void RaiseScore(int value)
16379 {
16380   local_player->score += value;
16381
16382 #if 1
16383   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16384
16385   DisplayGameControlValues();
16386 #else
16387   DrawGameValue_Score(local_player->score);
16388 #endif
16389 }
16390
16391 void RaiseScoreElement(int element)
16392 {
16393   switch (element)
16394   {
16395     case EL_EMERALD:
16396     case EL_BD_DIAMOND:
16397     case EL_EMERALD_YELLOW:
16398     case EL_EMERALD_RED:
16399     case EL_EMERALD_PURPLE:
16400     case EL_SP_INFOTRON:
16401       RaiseScore(level.score[SC_EMERALD]);
16402       break;
16403     case EL_DIAMOND:
16404       RaiseScore(level.score[SC_DIAMOND]);
16405       break;
16406     case EL_CRYSTAL:
16407       RaiseScore(level.score[SC_CRYSTAL]);
16408       break;
16409     case EL_PEARL:
16410       RaiseScore(level.score[SC_PEARL]);
16411       break;
16412     case EL_BUG:
16413     case EL_BD_BUTTERFLY:
16414     case EL_SP_ELECTRON:
16415       RaiseScore(level.score[SC_BUG]);
16416       break;
16417     case EL_SPACESHIP:
16418     case EL_BD_FIREFLY:
16419     case EL_SP_SNIKSNAK:
16420       RaiseScore(level.score[SC_SPACESHIP]);
16421       break;
16422     case EL_YAMYAM:
16423     case EL_DARK_YAMYAM:
16424       RaiseScore(level.score[SC_YAMYAM]);
16425       break;
16426     case EL_ROBOT:
16427       RaiseScore(level.score[SC_ROBOT]);
16428       break;
16429     case EL_PACMAN:
16430       RaiseScore(level.score[SC_PACMAN]);
16431       break;
16432     case EL_NUT:
16433       RaiseScore(level.score[SC_NUT]);
16434       break;
16435     case EL_DYNAMITE:
16436     case EL_EM_DYNAMITE:
16437     case EL_SP_DISK_RED:
16438     case EL_DYNABOMB_INCREASE_NUMBER:
16439     case EL_DYNABOMB_INCREASE_SIZE:
16440     case EL_DYNABOMB_INCREASE_POWER:
16441       RaiseScore(level.score[SC_DYNAMITE]);
16442       break;
16443     case EL_SHIELD_NORMAL:
16444     case EL_SHIELD_DEADLY:
16445       RaiseScore(level.score[SC_SHIELD]);
16446       break;
16447     case EL_EXTRA_TIME:
16448       RaiseScore(level.extra_time_score);
16449       break;
16450     case EL_KEY_1:
16451     case EL_KEY_2:
16452     case EL_KEY_3:
16453     case EL_KEY_4:
16454     case EL_EM_KEY_1:
16455     case EL_EM_KEY_2:
16456     case EL_EM_KEY_3:
16457     case EL_EM_KEY_4:
16458     case EL_EMC_KEY_5:
16459     case EL_EMC_KEY_6:
16460     case EL_EMC_KEY_7:
16461     case EL_EMC_KEY_8:
16462     case EL_DC_KEY_WHITE:
16463       RaiseScore(level.score[SC_KEY]);
16464       break;
16465     default:
16466       RaiseScore(element_info[element].collect_score);
16467       break;
16468   }
16469 }
16470
16471 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16472 {
16473   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16474   {
16475 #if defined(NETWORK_AVALIABLE)
16476     if (options.network)
16477       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16478     else
16479 #endif
16480     {
16481       if (quick_quit)
16482       {
16483 #if 1
16484
16485 #if 1
16486         FadeSkipNextFadeIn();
16487 #else
16488         fading = fading_none;
16489 #endif
16490
16491 #else
16492         OpenDoor(DOOR_CLOSE_1);
16493 #endif
16494
16495         game_status = GAME_MODE_MAIN;
16496
16497 #if 1
16498         DrawAndFadeInMainMenu(REDRAW_FIELD);
16499 #else
16500         DrawMainMenu();
16501 #endif
16502       }
16503       else
16504       {
16505 #if 0
16506         FadeOut(REDRAW_FIELD);
16507 #endif
16508
16509         game_status = GAME_MODE_MAIN;
16510
16511         DrawAndFadeInMainMenu(REDRAW_FIELD);
16512       }
16513     }
16514   }
16515   else          /* continue playing the game */
16516   {
16517     if (tape.playing && tape.deactivate_display)
16518       TapeDeactivateDisplayOff(TRUE);
16519
16520     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16521
16522     if (tape.playing && tape.deactivate_display)
16523       TapeDeactivateDisplayOn();
16524   }
16525 }
16526
16527 void RequestQuitGame(boolean ask_if_really_quit)
16528 {
16529   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16530   boolean skip_request = AllPlayersGone || quick_quit;
16531
16532   RequestQuitGameExt(skip_request, quick_quit,
16533                      "Do you really want to quit the game ?");
16534 }
16535
16536
16537 /* ------------------------------------------------------------------------- */
16538 /* random generator functions                                                */
16539 /* ------------------------------------------------------------------------- */
16540
16541 unsigned int InitEngineRandom_RND(int seed)
16542 {
16543   game.num_random_calls = 0;
16544
16545 #if 0
16546   unsigned int rnd_seed = InitEngineRandom(seed);
16547
16548   printf("::: START RND: %d\n", rnd_seed);
16549
16550   return rnd_seed;
16551 #else
16552
16553   return InitEngineRandom(seed);
16554
16555 #endif
16556
16557 }
16558
16559 unsigned int RND(int max)
16560 {
16561   if (max > 0)
16562   {
16563     game.num_random_calls++;
16564
16565     return GetEngineRandom(max);
16566   }
16567
16568   return 0;
16569 }
16570
16571
16572 /* ------------------------------------------------------------------------- */
16573 /* game engine snapshot handling functions                                   */
16574 /* ------------------------------------------------------------------------- */
16575
16576 struct EngineSnapshotInfo
16577 {
16578   /* runtime values for custom element collect score */
16579   int collect_score[NUM_CUSTOM_ELEMENTS];
16580
16581   /* runtime values for group element choice position */
16582   int choice_pos[NUM_GROUP_ELEMENTS];
16583
16584   /* runtime values for belt position animations */
16585   int belt_graphic[4][NUM_BELT_PARTS];
16586   int belt_anim_mode[4][NUM_BELT_PARTS];
16587 };
16588
16589 static struct EngineSnapshotInfo engine_snapshot_rnd;
16590 static char *snapshot_level_identifier = NULL;
16591 static int snapshot_level_nr = -1;
16592
16593 static void SaveEngineSnapshotValues_RND()
16594 {
16595   static int belt_base_active_element[4] =
16596   {
16597     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16598     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16599     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16600     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16601   };
16602   int i, j;
16603
16604   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16605   {
16606     int element = EL_CUSTOM_START + i;
16607
16608     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16609   }
16610
16611   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16612   {
16613     int element = EL_GROUP_START + i;
16614
16615     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16616   }
16617
16618   for (i = 0; i < 4; i++)
16619   {
16620     for (j = 0; j < NUM_BELT_PARTS; j++)
16621     {
16622       int element = belt_base_active_element[i] + j;
16623       int graphic = el2img(element);
16624       int anim_mode = graphic_info[graphic].anim_mode;
16625
16626       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16627       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16628     }
16629   }
16630 }
16631
16632 static void LoadEngineSnapshotValues_RND()
16633 {
16634   unsigned int num_random_calls = game.num_random_calls;
16635   int i, j;
16636
16637   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16638   {
16639     int element = EL_CUSTOM_START + i;
16640
16641     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16642   }
16643
16644   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16645   {
16646     int element = EL_GROUP_START + i;
16647
16648     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16649   }
16650
16651   for (i = 0; i < 4; i++)
16652   {
16653     for (j = 0; j < NUM_BELT_PARTS; j++)
16654     {
16655       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16656       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16657
16658       graphic_info[graphic].anim_mode = anim_mode;
16659     }
16660   }
16661
16662   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16663   {
16664     InitRND(tape.random_seed);
16665     for (i = 0; i < num_random_calls; i++)
16666       RND(1);
16667   }
16668
16669   if (game.num_random_calls != num_random_calls)
16670   {
16671     Error(ERR_INFO, "number of random calls out of sync");
16672     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16673     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16674     Error(ERR_EXIT, "this should not happen -- please debug");
16675   }
16676 }
16677
16678 void SaveEngineSnapshot()
16679 {
16680   /* do not save snapshots from editor */
16681   if (level_editor_test_game)
16682     return;
16683
16684   /* free previous snapshot buffers, if needed */
16685   FreeEngineSnapshotBuffers();
16686
16687   /* copy some special values to a structure better suited for the snapshot */
16688
16689   SaveEngineSnapshotValues_RND();
16690   SaveEngineSnapshotValues_EM();
16691   SaveEngineSnapshotValues_SP();
16692
16693   /* save values stored in special snapshot structure */
16694
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16698
16699   /* save further RND engine values */
16700
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16704
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16709
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16715
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16719
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16721
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16723
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16726
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16734   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16737   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16741   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16742   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16743   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16745
16746   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16747   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16748
16749   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16750   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16751   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16752
16753   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16754   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16755
16756   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16757   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16758   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16759   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16760   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16761
16762   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16763   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16764
16765   /* save level identification information */
16766
16767   setString(&snapshot_level_identifier, leveldir_current->identifier);
16768   snapshot_level_nr = level_nr;
16769
16770 #if 0
16771   ListNode *node = engine_snapshot_list_rnd;
16772   int num_bytes = 0;
16773
16774   while (node != NULL)
16775   {
16776     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16777
16778     node = node->next;
16779   }
16780
16781   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16782 #endif
16783 }
16784
16785 void LoadEngineSnapshot()
16786 {
16787   /* restore generically stored snapshot buffers */
16788
16789   LoadEngineSnapshotBuffers();
16790
16791   /* restore special values from snapshot structure */
16792
16793   LoadEngineSnapshotValues_RND();
16794   LoadEngineSnapshotValues_EM();
16795   LoadEngineSnapshotValues_SP();
16796 }
16797
16798 boolean CheckEngineSnapshot()
16799 {
16800   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16801           snapshot_level_nr == level_nr);
16802 }
16803
16804
16805 /* ---------- new game button stuff ---------------------------------------- */
16806
16807 static struct
16808 {
16809   int graphic;
16810   struct Rect *pos;
16811   int gadget_id;
16812   char *infotext;
16813 } gamebutton_info[NUM_GAME_BUTTONS] =
16814 {
16815   {
16816     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16817     GAME_CTRL_ID_STOP,                  "stop game"
16818   },
16819   {
16820     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16821     GAME_CTRL_ID_PAUSE,                 "pause game"
16822   },
16823   {
16824     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16825     GAME_CTRL_ID_PLAY,                  "play game"
16826   },
16827   {
16828     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16829     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16830   },
16831   {
16832     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16833     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16834   },
16835   {
16836     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16837     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16838   }
16839 };
16840
16841 void CreateGameButtons()
16842 {
16843   int i;
16844
16845   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16846   {
16847     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16848     struct Rect *pos = gamebutton_info[i].pos;
16849     struct GadgetInfo *gi;
16850     int button_type;
16851     boolean checked;
16852     unsigned int event_mask;
16853     int gd_x   = gfx->src_x;
16854     int gd_y   = gfx->src_y;
16855     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16856     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16857     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16858     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16859     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16860     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16861     int id = i;
16862
16863     if (id == GAME_CTRL_ID_STOP ||
16864         id == GAME_CTRL_ID_PAUSE ||
16865         id == GAME_CTRL_ID_PLAY)
16866     {
16867       button_type = GD_TYPE_NORMAL_BUTTON;
16868       checked = FALSE;
16869       event_mask = GD_EVENT_RELEASED;
16870     }
16871     else
16872     {
16873       button_type = GD_TYPE_CHECK_BUTTON;
16874       checked =
16875         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16876          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16877          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16878       event_mask = GD_EVENT_PRESSED;
16879     }
16880
16881     gi = CreateGadget(GDI_CUSTOM_ID, id,
16882                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16883                       GDI_X, DX + pos->x,
16884                       GDI_Y, DY + pos->y,
16885                       GDI_WIDTH, gfx->width,
16886                       GDI_HEIGHT, gfx->height,
16887                       GDI_TYPE, button_type,
16888                       GDI_STATE, GD_BUTTON_UNPRESSED,
16889                       GDI_CHECKED, checked,
16890                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16891                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16892                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16893                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16894                       GDI_DIRECT_DRAW, FALSE,
16895                       GDI_EVENT_MASK, event_mask,
16896                       GDI_CALLBACK_ACTION, HandleGameButtons,
16897                       GDI_END);
16898
16899     if (gi == NULL)
16900       Error(ERR_EXIT, "cannot create gadget");
16901
16902     game_gadget[id] = gi;
16903   }
16904 }
16905
16906 void FreeGameButtons()
16907 {
16908   int i;
16909
16910   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16911     FreeGadget(game_gadget[i]);
16912 }
16913
16914 static void MapGameButtons()
16915 {
16916   int i;
16917
16918   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16919     MapGadget(game_gadget[i]);
16920 }
16921
16922 void UnmapGameButtons()
16923 {
16924   int i;
16925
16926   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16927     UnmapGadget(game_gadget[i]);
16928 }
16929
16930 void RedrawGameButtons()
16931 {
16932   int i;
16933
16934   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16935     RedrawGadget(game_gadget[i]);
16936 }
16937
16938 static void HandleGameButtonsExt(int id)
16939 {
16940   if (game_status != GAME_MODE_PLAYING)
16941     return;
16942
16943   switch (id)
16944   {
16945     case GAME_CTRL_ID_STOP:
16946       if (tape.playing)
16947         TapeStop();
16948       else
16949         RequestQuitGame(TRUE);
16950       break;
16951
16952     case GAME_CTRL_ID_PAUSE:
16953       if (options.network)
16954       {
16955 #if defined(NETWORK_AVALIABLE)
16956         if (tape.pausing)
16957           SendToServer_ContinuePlaying();
16958         else
16959           SendToServer_PausePlaying();
16960 #endif
16961       }
16962       else
16963         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16964       break;
16965
16966     case GAME_CTRL_ID_PLAY:
16967       if (tape.pausing)
16968       {
16969 #if defined(NETWORK_AVALIABLE)
16970         if (options.network)
16971           SendToServer_ContinuePlaying();
16972         else
16973 #endif
16974         {
16975           tape.pausing = FALSE;
16976           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16977         }
16978       }
16979       break;
16980
16981     case SOUND_CTRL_ID_MUSIC:
16982       if (setup.sound_music)
16983       { 
16984         setup.sound_music = FALSE;
16985
16986         FadeMusic();
16987       }
16988       else if (audio.music_available)
16989       { 
16990         setup.sound = setup.sound_music = TRUE;
16991
16992         SetAudioMode(setup.sound);
16993
16994         PlayLevelMusic();
16995       }
16996       break;
16997
16998     case SOUND_CTRL_ID_LOOPS:
16999       if (setup.sound_loops)
17000         setup.sound_loops = FALSE;
17001       else if (audio.loops_available)
17002       {
17003         setup.sound = setup.sound_loops = TRUE;
17004
17005         SetAudioMode(setup.sound);
17006       }
17007       break;
17008
17009     case SOUND_CTRL_ID_SIMPLE:
17010       if (setup.sound_simple)
17011         setup.sound_simple = FALSE;
17012       else if (audio.sound_available)
17013       {
17014         setup.sound = setup.sound_simple = TRUE;
17015
17016         SetAudioMode(setup.sound);
17017       }
17018       break;
17019
17020     default:
17021       break;
17022   }
17023 }
17024
17025 static void HandleGameButtons(struct GadgetInfo *gi)
17026 {
17027   HandleGameButtonsExt(gi->custom_id);
17028 }
17029
17030 void HandleSoundButtonKeys(Key key)
17031 {
17032 #if 1
17033   if (key == setup.shortcut.sound_simple)
17034     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17035   else if (key == setup.shortcut.sound_loops)
17036     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17037   else if (key == setup.shortcut.sound_music)
17038     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17039 #else
17040   if (key == setup.shortcut.sound_simple)
17041     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17042   else if (key == setup.shortcut.sound_loops)
17043     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17044   else if (key == setup.shortcut.sound_music)
17045     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17046 #endif
17047 }