rnd-20080426-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_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(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_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(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_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172
1173 /* ------------------------------------------------------------------------- */
1174 /* definition of elements that automatically change to other elements after  */
1175 /* a specified time, eventually calling a function when changing             */
1176 /* ------------------------------------------------------------------------- */
1177
1178 /* forward declaration for changer functions */
1179 static void InitBuggyBase(int, int);
1180 static void WarnBuggyBase(int, int);
1181
1182 static void InitTrap(int, int);
1183 static void ActivateTrap(int, int);
1184 static void ChangeActiveTrap(int, int);
1185
1186 static void InitRobotWheel(int, int);
1187 static void RunRobotWheel(int, int);
1188 static void StopRobotWheel(int, int);
1189
1190 static void InitTimegateWheel(int, int);
1191 static void RunTimegateWheel(int, int);
1192
1193 static void InitMagicBallDelay(int, int);
1194 static void ActivateMagicBall(int, int);
1195
1196 struct ChangingElementInfo
1197 {
1198   int element;
1199   int target_element;
1200   int change_delay;
1201   void (*pre_change_function)(int x, int y);
1202   void (*change_function)(int x, int y);
1203   void (*post_change_function)(int x, int y);
1204 };
1205
1206 static struct ChangingElementInfo change_delay_list[] =
1207 {
1208   {
1209     EL_NUT_BREAKING,
1210     EL_EMERALD,
1211     6,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_PEARL_BREAKING,
1218     EL_EMPTY,
1219     8,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EXIT_OPENING,
1226     EL_EXIT_OPEN,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_CLOSING,
1234     EL_EXIT_CLOSED,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_STEEL_EXIT_OPENING,
1242     EL_STEEL_EXIT_OPEN,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_CLOSING,
1250     EL_STEEL_EXIT_CLOSED,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_EM_EXIT_OPENING,
1258     EL_EM_EXIT_OPEN,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_CLOSING,
1266 #if 1
1267     EL_EMPTY,
1268 #else
1269     EL_EM_EXIT_CLOSED,
1270 #endif
1271     29,
1272     NULL,
1273     NULL,
1274     NULL
1275   },
1276   {
1277     EL_EM_STEEL_EXIT_OPENING,
1278     EL_EM_STEEL_EXIT_OPEN,
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_CLOSING,
1286 #if 1
1287     EL_STEELWALL,
1288 #else
1289     EL_EM_STEEL_EXIT_CLOSED,
1290 #endif
1291     29,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_EXIT_OPENING,
1298     EL_SP_EXIT_OPEN,
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_CLOSING,
1306     EL_SP_EXIT_CLOSED,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SWITCHGATE_OPENING,
1314     EL_SWITCHGATE_OPEN,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_CLOSING,
1322     EL_SWITCHGATE_CLOSED,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_TIMEGATE_OPENING,
1330     EL_TIMEGATE_OPEN,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_CLOSING,
1338     EL_TIMEGATE_CLOSED,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344
1345   {
1346     EL_ACID_SPLASH_LEFT,
1347     EL_EMPTY,
1348     8,
1349     NULL,
1350     NULL,
1351     NULL
1352   },
1353   {
1354     EL_ACID_SPLASH_RIGHT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_SP_BUGGY_BASE,
1363     EL_SP_BUGGY_BASE_ACTIVATING,
1364     0,
1365     InitBuggyBase,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     EL_SP_BUGGY_BASE_ACTIVE,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     EL_SP_BUGGY_BASE,
1380     0,
1381     InitBuggyBase,
1382     WarnBuggyBase,
1383     NULL
1384   },
1385   {
1386     EL_TRAP,
1387     EL_TRAP_ACTIVE,
1388     0,
1389     InitTrap,
1390     NULL,
1391     ActivateTrap
1392   },
1393   {
1394     EL_TRAP_ACTIVE,
1395     EL_TRAP,
1396     31,
1397     NULL,
1398     ChangeActiveTrap,
1399     NULL
1400   },
1401   {
1402     EL_ROBOT_WHEEL_ACTIVE,
1403     EL_ROBOT_WHEEL,
1404     0,
1405     InitRobotWheel,
1406     RunRobotWheel,
1407     StopRobotWheel
1408   },
1409   {
1410     EL_TIMEGATE_SWITCH_ACTIVE,
1411     EL_TIMEGATE_SWITCH,
1412     0,
1413     InitTimegateWheel,
1414     RunTimegateWheel,
1415     NULL
1416   },
1417   {
1418     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1419     EL_DC_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_EMC_MAGIC_BALL_ACTIVE,
1427     EL_EMC_MAGIC_BALL_ACTIVE,
1428     0,
1429     InitMagicBallDelay,
1430     NULL,
1431     ActivateMagicBall
1432   },
1433   {
1434     EL_EMC_SPRING_BUMPER_ACTIVE,
1435     EL_EMC_SPRING_BUMPER,
1436     8,
1437     NULL,
1438     NULL,
1439     NULL
1440   },
1441   {
1442     EL_DIAGONAL_SHRINKING,
1443     EL_UNDEFINED,
1444     0,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_GROWING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL,
1456   },
1457
1458   {
1459     EL_UNDEFINED,
1460     EL_UNDEFINED,
1461     -1,
1462     NULL,
1463     NULL,
1464     NULL
1465   }
1466 };
1467
1468 struct
1469 {
1470   int element;
1471   int push_delay_fixed, push_delay_random;
1472 }
1473 push_delay_list[] =
1474 {
1475   { EL_SPRING,                  0, 0 },
1476   { EL_BALLOON,                 0, 0 },
1477
1478   { EL_SOKOBAN_OBJECT,          2, 0 },
1479   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1480   { EL_SATELLITE,               2, 0 },
1481   { EL_SP_DISK_YELLOW,          2, 0 },
1482
1483   { EL_UNDEFINED,               0, 0 },
1484 };
1485
1486 struct
1487 {
1488   int element;
1489   int move_stepsize;
1490 }
1491 move_stepsize_list[] =
1492 {
1493   { EL_AMOEBA_DROP,             2 },
1494   { EL_AMOEBA_DROPPING,         2 },
1495   { EL_QUICKSAND_FILLING,       1 },
1496   { EL_QUICKSAND_EMPTYING,      1 },
1497   { EL_QUICKSAND_FAST_FILLING,  2 },
1498   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1499   { EL_MAGIC_WALL_FILLING,      2 },
1500   { EL_MAGIC_WALL_EMPTYING,     2 },
1501   { EL_BD_MAGIC_WALL_FILLING,   2 },
1502   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1503   { EL_DC_MAGIC_WALL_FILLING,   2 },
1504   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1505
1506   { EL_UNDEFINED,               0 },
1507 };
1508
1509 struct
1510 {
1511   int element;
1512   int count;
1513 }
1514 collect_count_list[] =
1515 {
1516   { EL_EMERALD,                 1 },
1517   { EL_BD_DIAMOND,              1 },
1518   { EL_EMERALD_YELLOW,          1 },
1519   { EL_EMERALD_RED,             1 },
1520   { EL_EMERALD_PURPLE,          1 },
1521   { EL_DIAMOND,                 3 },
1522   { EL_SP_INFOTRON,             1 },
1523   { EL_PEARL,                   5 },
1524   { EL_CRYSTAL,                 8 },
1525
1526   { EL_UNDEFINED,               0 },
1527 };
1528
1529 struct
1530 {
1531   int element;
1532   int direction;
1533 }
1534 access_direction_list[] =
1535 {
1536   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1537   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1538   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1539   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1540   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1542   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1543   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1544   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1545   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1546   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1547
1548   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1549   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1550   { EL_SP_PORT_UP,                                                   MV_DOWN },
1551   { EL_SP_PORT_DOWN,                                         MV_UP           },
1552   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1553   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1554   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1555   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1556   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1557   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1558   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1559   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1560   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1561   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1562   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1563   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1564   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1565   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1566   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1567
1568   { EL_UNDEFINED,                       MV_NONE                              }
1569 };
1570
1571 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1572
1573 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1574 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1575 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1576                                  IS_JUST_CHANGING(x, y))
1577
1578 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1579
1580 /* static variables for playfield scan mode (scanning forward or backward) */
1581 static int playfield_scan_start_x = 0;
1582 static int playfield_scan_start_y = 0;
1583 static int playfield_scan_delta_x = 1;
1584 static int playfield_scan_delta_y = 1;
1585
1586 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1587                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1588                                      (y) += playfield_scan_delta_y)     \
1589                                 for ((x) = playfield_scan_start_x;      \
1590                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1591                                      (x) += playfield_scan_delta_x)
1592
1593 #ifdef DEBUG
1594 void DEBUG_SetMaximumDynamite()
1595 {
1596   int i;
1597
1598   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1599     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1600       local_player->inventory_element[local_player->inventory_size++] =
1601         EL_DYNAMITE;
1602 }
1603 #endif
1604
1605 static void InitPlayfieldScanModeVars()
1606 {
1607   if (game.use_reverse_scan_direction)
1608   {
1609     playfield_scan_start_x = lev_fieldx - 1;
1610     playfield_scan_start_y = lev_fieldy - 1;
1611
1612     playfield_scan_delta_x = -1;
1613     playfield_scan_delta_y = -1;
1614   }
1615   else
1616   {
1617     playfield_scan_start_x = 0;
1618     playfield_scan_start_y = 0;
1619
1620     playfield_scan_delta_x = 1;
1621     playfield_scan_delta_y = 1;
1622   }
1623 }
1624
1625 static void InitPlayfieldScanMode(int mode)
1626 {
1627   game.use_reverse_scan_direction =
1628     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1629
1630   InitPlayfieldScanModeVars();
1631 }
1632
1633 static int get_move_delay_from_stepsize(int move_stepsize)
1634 {
1635   move_stepsize =
1636     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1637
1638   /* make sure that stepsize value is always a power of 2 */
1639   move_stepsize = (1 << log_2(move_stepsize));
1640
1641   return TILEX / move_stepsize;
1642 }
1643
1644 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1645                                boolean init_game)
1646 {
1647   int player_nr = player->index_nr;
1648   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1649   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1650
1651   /* do no immediately change move delay -- the player might just be moving */
1652   player->move_delay_value_next = move_delay;
1653
1654   /* information if player can move must be set separately */
1655   player->cannot_move = cannot_move;
1656
1657   if (init_game)
1658   {
1659     player->move_delay       = game.initial_move_delay[player_nr];
1660     player->move_delay_value = game.initial_move_delay_value[player_nr];
1661
1662     player->move_delay_value_next = -1;
1663
1664     player->move_delay_reset_counter = 0;
1665   }
1666 }
1667
1668 void GetPlayerConfig()
1669 {
1670   GameFrameDelay = setup.game_frame_delay;
1671
1672   if (!audio.sound_available)
1673     setup.sound_simple = FALSE;
1674
1675   if (!audio.loops_available)
1676     setup.sound_loops = FALSE;
1677
1678   if (!audio.music_available)
1679     setup.sound_music = FALSE;
1680
1681   if (!video.fullscreen_available)
1682     setup.fullscreen = FALSE;
1683
1684   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1685
1686   SetAudioMode(setup.sound);
1687   InitJoysticks();
1688 }
1689
1690 int GetElementFromGroupElement(int element)
1691 {
1692   if (IS_GROUP_ELEMENT(element))
1693   {
1694     struct ElementGroupInfo *group = element_info[element].group;
1695     int last_anim_random_frame = gfx.anim_random_frame;
1696     int element_pos;
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = RND(group->num_elements_resolved);
1700
1701     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1702                                     group->choice_mode, 0,
1703                                     group->choice_pos);
1704
1705     if (group->choice_mode == ANIM_RANDOM)
1706       gfx.anim_random_frame = last_anim_random_frame;
1707
1708     group->choice_pos++;
1709
1710     element = group->element_resolved[element_pos];
1711   }
1712
1713   return element;
1714 }
1715
1716 static void InitPlayerField(int x, int y, int element, boolean init_game)
1717 {
1718   if (element == EL_SP_MURPHY)
1719   {
1720     if (init_game)
1721     {
1722       if (stored_player[0].present)
1723       {
1724         Feld[x][y] = EL_SP_MURPHY_CLONE;
1725
1726         return;
1727       }
1728       else
1729       {
1730         stored_player[0].initial_element = element;
1731         stored_player[0].use_murphy = TRUE;
1732
1733         if (!level.use_artwork_element[0])
1734           stored_player[0].artwork_element = EL_SP_MURPHY;
1735       }
1736
1737       Feld[x][y] = EL_PLAYER_1;
1738     }
1739   }
1740
1741   if (init_game)
1742   {
1743     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1744     int jx = player->jx, jy = player->jy;
1745
1746     player->present = TRUE;
1747
1748     player->block_last_field = (element == EL_SP_MURPHY ?
1749                                 level.sp_block_last_field :
1750                                 level.block_last_field);
1751
1752     /* ---------- initialize player's last field block delay --------------- */
1753
1754     /* always start with reliable default value (no adjustment needed) */
1755     player->block_delay_adjustment = 0;
1756
1757     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1758     if (player->block_last_field && element == EL_SP_MURPHY)
1759       player->block_delay_adjustment = 1;
1760
1761     /* special case 2: in game engines before 3.1.1, blocking was different */
1762     if (game.use_block_last_field_bug)
1763       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1764
1765     if (!options.network || player->connected)
1766     {
1767       player->active = TRUE;
1768
1769       /* remove potentially duplicate players */
1770       if (StorePlayer[jx][jy] == Feld[x][y])
1771         StorePlayer[jx][jy] = 0;
1772
1773       StorePlayer[x][y] = Feld[x][y];
1774
1775       if (options.debug)
1776       {
1777         printf("Player %d activated.\n", player->element_nr);
1778         printf("[Local player is %d and currently %s.]\n",
1779                local_player->element_nr,
1780                local_player->active ? "active" : "not active");
1781       }
1782     }
1783
1784     Feld[x][y] = EL_EMPTY;
1785
1786     player->jx = player->last_jx = x;
1787     player->jy = player->last_jy = y;
1788   }
1789
1790 #if USE_PLAYER_REANIMATION
1791   if (!init_game)
1792   {
1793     int player_nr = GET_PLAYER_NR(element);
1794     struct PlayerInfo *player = &stored_player[player_nr];
1795
1796     if (player->active && player->killed)
1797       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1798   }
1799 #endif
1800 }
1801
1802 static void InitField(int x, int y, boolean init_game)
1803 {
1804   int element = Feld[x][y];
1805
1806   switch (element)
1807   {
1808     case EL_SP_MURPHY:
1809     case EL_PLAYER_1:
1810     case EL_PLAYER_2:
1811     case EL_PLAYER_3:
1812     case EL_PLAYER_4:
1813       InitPlayerField(x, y, element, init_game);
1814       break;
1815
1816     case EL_SOKOBAN_FIELD_PLAYER:
1817       element = Feld[x][y] = EL_PLAYER_1;
1818       InitField(x, y, init_game);
1819
1820       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1821       InitField(x, y, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_EMPTY:
1825       local_player->sokobanfields_still_needed++;
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       local_player->lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       local_player->friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    /* more than one switch -- set it like the first switch */
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952 #if !USE_BOTH_SWITCHGATE_SWITCHES
1953     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1954       if (init_game)
1955         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1956       break;
1957
1958     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1959       if (init_game)
1960         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1961       break;
1962 #endif
1963
1964     case EL_LIGHT_SWITCH_ACTIVE:
1965       if (init_game)
1966         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1967       break;
1968
1969     case EL_INVISIBLE_STEELWALL:
1970     case EL_INVISIBLE_WALL:
1971     case EL_INVISIBLE_SAND:
1972       if (game.light_time_left > 0 ||
1973           game.lenses_time_left > 0)
1974         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1975       break;
1976
1977     case EL_EMC_MAGIC_BALL:
1978       if (game.ball_state)
1979         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1980       break;
1981
1982     case EL_EMC_MAGIC_BALL_SWITCH:
1983       if (game.ball_state)
1984         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1985       break;
1986
1987     case EL_TRIGGER_PLAYER:
1988     case EL_TRIGGER_ELEMENT:
1989     case EL_TRIGGER_CE_VALUE:
1990     case EL_TRIGGER_CE_SCORE:
1991     case EL_SELF:
1992     case EL_ANY_ELEMENT:
1993     case EL_CURRENT_CE_VALUE:
1994     case EL_CURRENT_CE_SCORE:
1995     case EL_PREV_CE_1:
1996     case EL_PREV_CE_2:
1997     case EL_PREV_CE_3:
1998     case EL_PREV_CE_4:
1999     case EL_PREV_CE_5:
2000     case EL_PREV_CE_6:
2001     case EL_PREV_CE_7:
2002     case EL_PREV_CE_8:
2003     case EL_NEXT_CE_1:
2004     case EL_NEXT_CE_2:
2005     case EL_NEXT_CE_3:
2006     case EL_NEXT_CE_4:
2007     case EL_NEXT_CE_5:
2008     case EL_NEXT_CE_6:
2009     case EL_NEXT_CE_7:
2010     case EL_NEXT_CE_8:
2011       /* reference elements should not be used on the playfield */
2012       Feld[x][y] = EL_EMPTY;
2013       break;
2014
2015     default:
2016       if (IS_CUSTOM_ELEMENT(element))
2017       {
2018         if (CAN_MOVE(element))
2019           InitMovDir(x, y);
2020
2021 #if USE_NEW_CUSTOM_VALUE
2022         if (!element_info[element].use_last_ce_value || init_game)
2023           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2024 #endif
2025       }
2026       else if (IS_GROUP_ELEMENT(element))
2027       {
2028         Feld[x][y] = GetElementFromGroupElement(element);
2029
2030         InitField(x, y, init_game);
2031       }
2032
2033       break;
2034   }
2035
2036   if (!init_game)
2037     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2038 }
2039
2040 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2041 {
2042   InitField(x, y, init_game);
2043
2044   /* not needed to call InitMovDir() -- already done by InitField()! */
2045   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046       CAN_MOVE(Feld[x][y]))
2047     InitMovDir(x, y);
2048 }
2049
2050 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2051 {
2052   int old_element = Feld[x][y];
2053
2054   InitField(x, y, init_game);
2055
2056   /* not needed to call InitMovDir() -- already done by InitField()! */
2057   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2058       CAN_MOVE(old_element) &&
2059       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2060     InitMovDir(x, y);
2061
2062   /* this case is in fact a combination of not less than three bugs:
2063      first, it calls InitMovDir() for elements that can move, although this is
2064      already done by InitField(); then, it checks the element that was at this
2065      field _before_ the call to InitField() (which can change it); lastly, it
2066      was not called for "mole with direction" elements, which were treated as
2067      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2068   */
2069 }
2070
2071 #if 1
2072
2073 static int get_key_element_from_nr(int key_nr)
2074 {
2075   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2076                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2077                           EL_EM_KEY_1 : EL_KEY_1);
2078
2079   return key_base_element + key_nr;
2080 }
2081
2082 static int get_next_dropped_element(struct PlayerInfo *player)
2083 {
2084   return (player->inventory_size > 0 ?
2085           player->inventory_element[player->inventory_size - 1] :
2086           player->inventory_infinite_element != EL_UNDEFINED ?
2087           player->inventory_infinite_element :
2088           player->dynabombs_left > 0 ?
2089           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2090           EL_UNDEFINED);
2091 }
2092
2093 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2094 {
2095   /* pos >= 0: get element from bottom of the stack;
2096      pos <  0: get element from top of the stack */
2097
2098   if (pos < 0)
2099   {
2100     int min_inventory_size = -pos;
2101     int inventory_pos = player->inventory_size - min_inventory_size;
2102     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2103
2104     return (player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             player->inventory_infinite_element != EL_UNDEFINED ?
2107             player->inventory_infinite_element :
2108             player->dynabombs_left >= min_dynabombs_left ?
2109             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2110             EL_UNDEFINED);
2111   }
2112   else
2113   {
2114     int min_dynabombs_left = pos + 1;
2115     int min_inventory_size = pos + 1 - player->dynabombs_left;
2116     int inventory_pos = pos - player->dynabombs_left;
2117
2118     return (player->inventory_infinite_element != EL_UNDEFINED ?
2119             player->inventory_infinite_element :
2120             player->dynabombs_left >= min_dynabombs_left ?
2121             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122             player->inventory_size >= min_inventory_size ?
2123             player->inventory_element[inventory_pos] :
2124             EL_UNDEFINED);
2125   }
2126 }
2127
2128 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2129 {
2130   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2131   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2132   int compare_result;
2133
2134   if (gpo1->sort_priority != gpo2->sort_priority)
2135     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2136   else
2137     compare_result = gpo1->nr - gpo2->nr;
2138
2139   return compare_result;
2140 }
2141
2142 void InitGameControlValues()
2143 {
2144   int i;
2145
2146   for (i = 0; game_panel_controls[i].nr != -1; i++)
2147   {
2148     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2149     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2150     struct TextPosInfo *pos = gpc->pos;
2151     int nr = gpc->nr;
2152     int type = gpc->type;
2153
2154     if (nr != i)
2155     {
2156       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2157       Error(ERR_EXIT, "this should not happen -- please debug");
2158     }
2159
2160     /* force update of game controls after initialization */
2161     gpc->value = gpc->last_value = -1;
2162     gpc->frame = gpc->last_frame = -1;
2163     gpc->gfx_frame = -1;
2164
2165     /* determine panel value width for later calculation of alignment */
2166     if (type == TYPE_INTEGER || type == TYPE_STRING)
2167     {
2168       pos->width = pos->size * getFontWidth(pos->font);
2169       pos->height = getFontHeight(pos->font);
2170     }
2171     else if (type == TYPE_ELEMENT)
2172     {
2173       pos->width = pos->size;
2174       pos->height = pos->size;
2175     }
2176
2177     /* fill structure for game panel draw order */
2178     gpo->nr = gpc->nr;
2179     gpo->sort_priority = pos->sort_priority;
2180   }
2181
2182   /* sort game panel controls according to sort_priority and control number */
2183   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2184         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2185 }
2186
2187 void UpdatePlayfieldElementCount()
2188 {
2189   boolean use_element_count = FALSE;
2190   int i, j, x, y;
2191
2192   /* first check if it is needed at all to calculate playfield element count */
2193   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2194     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2195       use_element_count = TRUE;
2196
2197   if (!use_element_count)
2198     return;
2199
2200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2201     element_info[i].element_count = 0;
2202
2203   SCAN_PLAYFIELD(x, y)
2204   {
2205     element_info[Feld[x][y]].element_count++;
2206   }
2207
2208   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2209     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2210       if (IS_IN_GROUP(j, i))
2211         element_info[EL_GROUP_START + i].element_count +=
2212           element_info[j].element_count;
2213 }
2214
2215 void UpdateGameControlValues()
2216 {
2217   int i, k;
2218   int time = (local_player->LevelSolved ?
2219               local_player->LevelSolved_CountingTime :
2220               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2221               level.native_em_level->lev->time :
2222               level.time == 0 ? TimePlayed : TimeLeft);
2223   int score = (local_player->LevelSolved ?
2224                local_player->LevelSolved_CountingScore :
2225                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226                level.native_em_level->lev->score :
2227                local_player->score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               local_player->gems_still_needed);
2231   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232                      level.native_em_level->lev->required > 0 :
2233                      local_player->gems_still_needed > 0 ||
2234                      local_player->sokobanfields_still_needed > 0 ||
2235                      local_player->lights_still_needed > 0);
2236
2237   UpdatePlayfieldElementCount();
2238
2239   /* update game panel control values */
2240
2241   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2242   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2243
2244   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2245   for (i = 0; i < MAX_NUM_KEYS; i++)
2246     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2247   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2248   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2249
2250   if (game.centered_player_nr == -1)
2251   {
2252     for (i = 0; i < MAX_PLAYERS; i++)
2253     {
2254       for (k = 0; k < MAX_NUM_KEYS; k++)
2255       {
2256         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2257         {
2258           if (level.native_em_level->ply[i]->keys & (1 << k))
2259             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2260               get_key_element_from_nr(k);
2261         }
2262         else if (stored_player[i].key[k])
2263           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2264             get_key_element_from_nr(k);
2265       }
2266
2267       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2268         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2269           level.native_em_level->ply[i]->dynamite;
2270       else
2271         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2272           stored_player[i].inventory_size;
2273
2274       if (stored_player[i].num_white_keys > 0)
2275         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2276           EL_DC_KEY_WHITE;
2277
2278       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2279         stored_player[i].num_white_keys;
2280     }
2281   }
2282   else
2283   {
2284     int player_nr = game.centered_player_nr;
2285
2286     for (k = 0; k < MAX_NUM_KEYS; k++)
2287     {
2288       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289       {
2290         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2291           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2292             get_key_element_from_nr(k);
2293       }
2294       else if (stored_player[player_nr].key[k])
2295         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2296           get_key_element_from_nr(k);
2297     }
2298
2299     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2300       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2301         level.native_em_level->ply[player_nr]->dynamite;
2302     else
2303       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2304         stored_player[player_nr].inventory_size;
2305
2306     if (stored_player[player_nr].num_white_keys > 0)
2307       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2308
2309     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2310       stored_player[player_nr].num_white_keys;
2311   }
2312
2313   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2314   {
2315     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2316       get_inventory_element_from_pos(local_player, i);
2317     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, -i - 1);
2319   }
2320
2321   game_panel_controls[GAME_PANEL_SCORE].value = score;
2322   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2323
2324   game_panel_controls[GAME_PANEL_TIME].value = time;
2325
2326   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2327   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2328   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2329
2330   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2331     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2332      EL_EMPTY);
2333   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2334     local_player->shield_normal_time_left;
2335   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2336     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2337      EL_EMPTY);
2338   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2339     local_player->shield_deadly_time_left;
2340
2341   game_panel_controls[GAME_PANEL_EXIT].value =
2342     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2343
2344   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2345     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2348      EL_EMC_MAGIC_BALL_SWITCH);
2349
2350   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2351     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2353     game.light_time_left;
2354
2355   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2356     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2358     game.timegate_time_left;
2359
2360   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2361     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2362
2363   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2364     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2365   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2366     game.lenses_time_left;
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2369     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2371     game.magnify_time_left;
2372
2373   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2374     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2375      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2376      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2377      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2378      EL_BALLOON_SWITCH_NONE);
2379
2380   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2381     local_player->dynabomb_count;
2382   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2383     local_player->dynabomb_size;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2385     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2386
2387   game_panel_controls[GAME_PANEL_PENGUINS].value =
2388     local_player->friends_still_needed;
2389
2390   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2391     local_player->sokobanfields_still_needed;
2392   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2393     local_player->sokobanfields_still_needed;
2394
2395   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2396     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2397
2398   for (i = 0; i < NUM_BELTS; i++)
2399   {
2400     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2401       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2402        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2403     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2404       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2405   }
2406
2407   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2408     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2410     game.magic_wall_time_left;
2411
2412 #if USE_PLAYER_GRAVITY
2413   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2414     local_player->gravity;
2415 #else
2416   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2417 #endif
2418
2419   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2420     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2421
2422   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2423     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2424       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2425        game.panel.element[i].id : EL_UNDEFINED);
2426
2427   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2428     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2429       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2430        element_info[game.panel.element_count[i].id].element_count : 0);
2431
2432   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2433     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2434       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2435        element_info[game.panel.ce_score[i].id].collect_score : 0);
2436
2437   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2438     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2439       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2440        element_info[game.panel.ce_score_element[i].id].collect_score :
2441        EL_UNDEFINED);
2442
2443   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2444   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2445   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2446
2447   /* update game panel control frames */
2448
2449   for (i = 0; game_panel_controls[i].nr != -1; i++)
2450   {
2451     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2452
2453     if (gpc->type == TYPE_ELEMENT)
2454     {
2455       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2456       {
2457         int last_anim_random_frame = gfx.anim_random_frame;
2458         int element = gpc->value;
2459         int graphic = el2panelimg(element);
2460
2461         if (gpc->value != gpc->last_value)
2462         {
2463           gpc->gfx_frame = 0;
2464           gpc->gfx_random = INIT_GFX_RANDOM();
2465         }
2466         else
2467         {
2468           gpc->gfx_frame++;
2469
2470           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2471               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2472             gpc->gfx_random = INIT_GFX_RANDOM();
2473         }
2474
2475         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2476           gfx.anim_random_frame = gpc->gfx_random;
2477
2478         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2479           gpc->gfx_frame = element_info[element].collect_score;
2480
2481         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2482                                               gpc->gfx_frame);
2483
2484         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2485           gfx.anim_random_frame = last_anim_random_frame;
2486       }
2487     }
2488   }
2489 }
2490
2491 void DisplayGameControlValues()
2492 {
2493   boolean redraw_panel = FALSE;
2494   int i;
2495
2496   for (i = 0; game_panel_controls[i].nr != -1; i++)
2497   {
2498     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2499
2500     if (PANEL_DEACTIVATED(gpc->pos))
2501       continue;
2502
2503     if (gpc->value == gpc->last_value &&
2504         gpc->frame == gpc->last_frame)
2505       continue;
2506
2507     redraw_panel = TRUE;
2508   }
2509
2510   if (!redraw_panel)
2511     return;
2512
2513   /* copy default game door content to main double buffer */
2514   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516
2517   /* redraw game control buttons */
2518 #if 1
2519   RedrawGameButtons();
2520 #else
2521   UnmapGameButtons();
2522   MapGameButtons();
2523 #endif
2524
2525   game_status = GAME_MODE_PSEUDO_PANEL;
2526
2527 #if 1
2528   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2529 #else
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531 #endif
2532   {
2533 #if 1
2534     int nr = game_panel_order[i].nr;
2535     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2536 #else
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538     int nr = gpc->nr;
2539 #endif
2540     struct TextPosInfo *pos = gpc->pos;
2541     int type = gpc->type;
2542     int value = gpc->value;
2543     int frame = gpc->frame;
2544 #if 0
2545     int last_value = gpc->last_value;
2546     int last_frame = gpc->last_frame;
2547 #endif
2548     int size = pos->size;
2549     int font = pos->font;
2550     boolean draw_masked = pos->draw_masked;
2551     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2552
2553     if (PANEL_DEACTIVATED(pos))
2554       continue;
2555
2556 #if 0
2557     if (value == last_value && frame == last_frame)
2558       continue;
2559 #endif
2560
2561     gpc->last_value = value;
2562     gpc->last_frame = frame;
2563
2564 #if 0
2565     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2566 #endif
2567
2568     if (type == TYPE_INTEGER)
2569     {
2570       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571           nr == GAME_PANEL_TIME)
2572       {
2573         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2574
2575         if (use_dynamic_size)           /* use dynamic number of digits */
2576         {
2577           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579           int size2 = size1 + 1;
2580           int font1 = pos->font;
2581           int font2 = pos->font_alt;
2582
2583           size = (value < value_change ? size1 : size2);
2584           font = (value < value_change ? font1 : font2);
2585
2586 #if 0
2587           /* clear background if value just changed its size (dynamic digits) */
2588           if ((last_value < value_change) != (value < value_change))
2589           {
2590             int width1 = size1 * getFontWidth(font1);
2591             int width2 = size2 * getFontWidth(font2);
2592             int max_width = MAX(width1, width2);
2593             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2594
2595             pos->width = max_width;
2596
2597             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598                                        max_width, max_height);
2599           }
2600 #endif
2601         }
2602       }
2603
2604 #if 1
2605       /* correct text size if "digits" is zero or less */
2606       if (size <= 0)
2607         size = strlen(int2str(value, size));
2608
2609       /* dynamically correct text alignment */
2610       pos->width = size * getFontWidth(font);
2611 #endif
2612
2613       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2614                   int2str(value, size), font, mask_mode);
2615     }
2616     else if (type == TYPE_ELEMENT)
2617     {
2618       int element, graphic;
2619       Bitmap *src_bitmap;
2620       int src_x, src_y;
2621       int width, height;
2622       int dst_x = PANEL_XPOS(pos);
2623       int dst_y = PANEL_YPOS(pos);
2624
2625 #if 1
2626       if (value != EL_UNDEFINED && value != EL_EMPTY)
2627       {
2628         element = value;
2629         graphic = el2panelimg(value);
2630
2631         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2632
2633 #if 1
2634         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2635           size = TILESIZE;
2636 #endif
2637
2638         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2639                               &src_x, &src_y);
2640
2641         width  = graphic_info[graphic].width  * size / TILESIZE;
2642         height = graphic_info[graphic].height * size / TILESIZE;
2643
2644         if (draw_masked)
2645         {
2646           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2647                         dst_x - src_x, dst_y - src_y);
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         }
2651         else
2652         {
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655         }
2656       }
2657 #else
2658       if (value == EL_UNDEFINED || value == EL_EMPTY)
2659       {
2660         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2661         graphic = el2panelimg(element);
2662
2663         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2664         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2665         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2666       }
2667       else
2668       {
2669         element = value;
2670         graphic = el2panelimg(value);
2671
2672         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2673       }
2674
2675       width  = graphic_info[graphic].width  * size / TILESIZE;
2676       height = graphic_info[graphic].height * size / TILESIZE;
2677
2678       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2679 #endif
2680     }
2681     else if (type == TYPE_STRING)
2682     {
2683       boolean active = (value != 0);
2684       char *state_normal = "off";
2685       char *state_active = "on";
2686       char *state = (active ? state_active : state_normal);
2687       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2688                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2689                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2690                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2691
2692       if (nr == GAME_PANEL_GRAVITY_STATE)
2693       {
2694         int font1 = pos->font;          /* (used for normal state) */
2695         int font2 = pos->font_alt;      /* (used for active state) */
2696 #if 0
2697         int size1 = strlen(state_normal);
2698         int size2 = strlen(state_active);
2699         int width1 = size1 * getFontWidth(font1);
2700         int width2 = size2 * getFontWidth(font2);
2701         int max_width = MAX(width1, width2);
2702         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2703
2704         pos->width = max_width;
2705
2706         /* clear background for values that may have changed its size */
2707         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2708                                    max_width, max_height);
2709 #endif
2710
2711         font = (active ? font2 : font1);
2712       }
2713
2714       if (s != NULL)
2715       {
2716         char *s_cut;
2717
2718 #if 1
2719         if (size <= 0)
2720         {
2721           /* don't truncate output if "chars" is zero or less */
2722           size = strlen(s);
2723
2724           /* dynamically correct text alignment */
2725           pos->width = size * getFontWidth(font);
2726         }
2727 #endif
2728
2729         s_cut = getStringCopyN(s, size);
2730
2731         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2732                     s_cut, font, mask_mode);
2733
2734         free(s_cut);
2735       }
2736     }
2737
2738     redraw_mask |= REDRAW_DOOR_1;
2739   }
2740
2741   game_status = GAME_MODE_PLAYING;
2742 }
2743
2744 void UpdateAndDisplayGameControlValues()
2745 {
2746   if (tape.warp_forward)
2747     return;
2748
2749   UpdateGameControlValues();
2750   DisplayGameControlValues();
2751 }
2752
2753 void DrawGameValue_Emeralds(int value)
2754 {
2755   struct TextPosInfo *pos = &game.panel.gems;
2756 #if 1
2757   int font_nr = pos->font;
2758 #else
2759   int font_nr = FONT_TEXT_2;
2760 #endif
2761   int font_width = getFontWidth(font_nr);
2762   int chars = pos->size;
2763
2764 #if 1
2765   return;       /* !!! USE NEW STUFF !!! */
2766 #endif
2767
2768   if (PANEL_DEACTIVATED(pos))
2769     return;
2770
2771   pos->width = chars * font_width;
2772
2773   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2774 }
2775
2776 void DrawGameValue_Dynamite(int value)
2777 {
2778   struct TextPosInfo *pos = &game.panel.inventory_count;
2779 #if 1
2780   int font_nr = pos->font;
2781 #else
2782   int font_nr = FONT_TEXT_2;
2783 #endif
2784   int font_width = getFontWidth(font_nr);
2785   int chars = pos->size;
2786
2787 #if 1
2788   return;       /* !!! USE NEW STUFF !!! */
2789 #endif
2790
2791   if (PANEL_DEACTIVATED(pos))
2792     return;
2793
2794   pos->width = chars * font_width;
2795
2796   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2797 }
2798
2799 void DrawGameValue_Score(int value)
2800 {
2801   struct TextPosInfo *pos = &game.panel.score;
2802 #if 1
2803   int font_nr = pos->font;
2804 #else
2805   int font_nr = FONT_TEXT_2;
2806 #endif
2807   int font_width = getFontWidth(font_nr);
2808   int chars = pos->size;
2809
2810 #if 1
2811   return;       /* !!! USE NEW STUFF !!! */
2812 #endif
2813
2814   if (PANEL_DEACTIVATED(pos))
2815     return;
2816
2817   pos->width = chars * font_width;
2818
2819   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2820 }
2821
2822 void DrawGameValue_Time(int value)
2823 {
2824   struct TextPosInfo *pos = &game.panel.time;
2825   static int last_value = -1;
2826   int chars1 = 3;
2827   int chars2 = 4;
2828   int chars = pos->size;
2829 #if 1
2830   int font1_nr = pos->font;
2831   int font2_nr = pos->font_alt;
2832 #else
2833   int font1_nr = FONT_TEXT_2;
2834   int font2_nr = FONT_TEXT_1;
2835 #endif
2836   int font_nr = font1_nr;
2837   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2838
2839 #if 1
2840   return;       /* !!! USE NEW STUFF !!! */
2841 #endif
2842
2843   if (PANEL_DEACTIVATED(pos))
2844     return;
2845
2846   if (use_dynamic_chars)                /* use dynamic number of chars */
2847   {
2848     chars   = (value < 1000 ? chars1   : chars2);
2849     font_nr = (value < 1000 ? font1_nr : font2_nr);
2850   }
2851
2852   /* clear background if value just changed its size (dynamic chars only) */
2853   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2854   {
2855     int width1 = chars1 * getFontWidth(font1_nr);
2856     int width2 = chars2 * getFontWidth(font2_nr);
2857     int max_width = MAX(width1, width2);
2858     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2859
2860     pos->width = max_width;
2861
2862     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2863                                max_width, max_height);
2864   }
2865
2866   pos->width = chars * getFontWidth(font_nr);
2867
2868   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2869
2870   last_value = value;
2871 }
2872
2873 void DrawGameValue_Level(int value)
2874 {
2875   struct TextPosInfo *pos = &game.panel.level_number;
2876   int chars1 = 2;
2877   int chars2 = 3;
2878   int chars = pos->size;
2879 #if 1
2880   int font1_nr = pos->font;
2881   int font2_nr = pos->font_alt;
2882 #else
2883   int font1_nr = FONT_TEXT_2;
2884   int font2_nr = FONT_TEXT_1;
2885 #endif
2886   int font_nr = font1_nr;
2887   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2888
2889 #if 1
2890   return;       /* !!! USE NEW STUFF !!! */
2891 #endif
2892
2893   if (PANEL_DEACTIVATED(pos))
2894     return;
2895
2896   if (use_dynamic_chars)                /* use dynamic number of chars */
2897   {
2898     chars   = (level_nr < 100 ? chars1   : chars2);
2899     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2900   }
2901
2902   pos->width = chars * getFontWidth(font_nr);
2903
2904   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2905 }
2906
2907 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2908 {
2909 #if 0
2910   struct TextPosInfo *pos = &game.panel.keys;
2911 #endif
2912 #if 0
2913   int base_key_graphic = EL_KEY_1;
2914 #endif
2915   int i;
2916
2917 #if 1
2918   return;       /* !!! USE NEW STUFF !!! */
2919 #endif
2920
2921 #if 0
2922   if (PANEL_DEACTIVATED(pos))
2923     return;
2924 #endif
2925
2926 #if 0
2927   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2928     base_key_graphic = EL_EM_KEY_1;
2929 #endif
2930
2931 #if 0
2932   pos->width = 4 * MINI_TILEX;
2933 #endif
2934
2935 #if 1
2936   for (i = 0; i < MAX_NUM_KEYS; i++)
2937 #else
2938   /* currently only 4 of 8 possible keys are displayed */
2939   for (i = 0; i < STD_NUM_KEYS; i++)
2940 #endif
2941   {
2942 #if 1
2943     struct TextPosInfo *pos = &game.panel.key[i];
2944 #endif
2945     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2946     int src_y = DOOR_GFX_PAGEY1 + 123;
2947 #if 1
2948     int dst_x = PANEL_XPOS(pos);
2949     int dst_y = PANEL_YPOS(pos);
2950 #else
2951     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2952     int dst_y = PANEL_YPOS(pos);
2953 #endif
2954
2955 #if 1
2956     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2957                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2958                    EL_KEY_1) + i;
2959     int graphic = el2edimg(element);
2960 #endif
2961
2962 #if 1
2963     if (PANEL_DEACTIVATED(pos))
2964       continue;
2965 #endif
2966
2967 #if 0
2968     /* masked blit with tiles from half-size scaled bitmap does not work yet
2969        (no mask bitmap created for these sizes after loading and scaling) --
2970        solution: load without creating mask, scale, then create final mask */
2971
2972     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2973                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2974
2975     if (key[i])
2976     {
2977 #if 0
2978       int graphic = el2edimg(base_key_graphic + i);
2979 #endif
2980       Bitmap *src_bitmap;
2981       int src_x, src_y;
2982
2983       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2984
2985       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2986                     dst_x - src_x, dst_y - src_y);
2987       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2988                        dst_x, dst_y);
2989     }
2990 #else
2991 #if 1
2992     if (key[i])
2993       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2994     else
2995       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2996                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2997 #else
2998     if (key[i])
2999       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3000     else
3001       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3002                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3003 #endif
3004 #endif
3005   }
3006 }
3007
3008 #else
3009
3010 void DrawGameValue_Emeralds(int value)
3011 {
3012   int font_nr = FONT_TEXT_2;
3013   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3014
3015   if (PANEL_DEACTIVATED(game.panel.gems))
3016     return;
3017
3018   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3019 }
3020
3021 void DrawGameValue_Dynamite(int value)
3022 {
3023   int font_nr = FONT_TEXT_2;
3024   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3025
3026   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3027     return;
3028
3029   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3030 }
3031
3032 void DrawGameValue_Score(int value)
3033 {
3034   int font_nr = FONT_TEXT_2;
3035   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3036
3037   if (PANEL_DEACTIVATED(game.panel.score))
3038     return;
3039
3040   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3041 }
3042
3043 void DrawGameValue_Time(int value)
3044 {
3045   int font1_nr = FONT_TEXT_2;
3046 #if 1
3047   int font2_nr = FONT_TEXT_1;
3048 #else
3049   int font2_nr = FONT_LEVEL_NUMBER;
3050 #endif
3051   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3052   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3053
3054   if (PANEL_DEACTIVATED(game.panel.time))
3055     return;
3056
3057   /* clear background if value just changed its size */
3058   if (value == 999 || value == 1000)
3059     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3060
3061   if (value < 1000)
3062     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3063   else
3064     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3065 }
3066
3067 void DrawGameValue_Level(int value)
3068 {
3069   int font1_nr = FONT_TEXT_2;
3070 #if 1
3071   int font2_nr = FONT_TEXT_1;
3072 #else
3073   int font2_nr = FONT_LEVEL_NUMBER;
3074 #endif
3075
3076   if (PANEL_DEACTIVATED(game.panel.level))
3077     return;
3078
3079   if (level_nr < 100)
3080     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3081   else
3082     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3083 }
3084
3085 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3086 {
3087   int base_key_graphic = EL_KEY_1;
3088   int i;
3089
3090   if (PANEL_DEACTIVATED(game.panel.keys))
3091     return;
3092
3093   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3094     base_key_graphic = EL_EM_KEY_1;
3095
3096   /* currently only 4 of 8 possible keys are displayed */
3097   for (i = 0; i < STD_NUM_KEYS; i++)
3098   {
3099     int x = XX_KEYS + i * MINI_TILEX;
3100     int y = YY_KEYS;
3101
3102     if (key[i])
3103       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3104     else
3105       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3106                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3107   }
3108 }
3109
3110 #endif
3111
3112 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3113                        int key_bits)
3114 {
3115   int key[MAX_NUM_KEYS];
3116   int i;
3117
3118   /* prevent EM engine from updating time/score values parallel to GameWon() */
3119   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3120       local_player->LevelSolved)
3121     return;
3122
3123   for (i = 0; i < MAX_NUM_KEYS; i++)
3124     key[i] = key_bits & (1 << i);
3125
3126   DrawGameValue_Level(level_nr);
3127
3128   DrawGameValue_Emeralds(emeralds);
3129   DrawGameValue_Dynamite(dynamite);
3130   DrawGameValue_Score(score);
3131   DrawGameValue_Time(time);
3132
3133   DrawGameValue_Keys(key);
3134 }
3135
3136 void UpdateGameDoorValues()
3137 {
3138   UpdateGameControlValues();
3139 }
3140
3141 void DrawGameDoorValues()
3142 {
3143   DisplayGameControlValues();
3144 }
3145
3146 void DrawGameDoorValues_OLD()
3147 {
3148   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3149   int dynamite_value = 0;
3150   int score_value = (local_player->LevelSolved ? local_player->score_final :
3151                      local_player->score);
3152   int gems_value = local_player->gems_still_needed;
3153   int key_bits = 0;
3154   int i, j;
3155
3156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3157   {
3158     DrawGameDoorValues_EM();
3159
3160     return;
3161   }
3162
3163   if (game.centered_player_nr == -1)
3164   {
3165     for (i = 0; i < MAX_PLAYERS; i++)
3166     {
3167       for (j = 0; j < MAX_NUM_KEYS; j++)
3168         if (stored_player[i].key[j])
3169           key_bits |= (1 << j);
3170
3171       dynamite_value += stored_player[i].inventory_size;
3172     }
3173   }
3174   else
3175   {
3176     int player_nr = game.centered_player_nr;
3177
3178     for (i = 0; i < MAX_NUM_KEYS; i++)
3179       if (stored_player[player_nr].key[i])
3180         key_bits |= (1 << i);
3181
3182     dynamite_value = stored_player[player_nr].inventory_size;
3183   }
3184
3185   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3186                     key_bits);
3187 }
3188
3189
3190 /*
3191   =============================================================================
3192   InitGameEngine()
3193   -----------------------------------------------------------------------------
3194   initialize game engine due to level / tape version number
3195   =============================================================================
3196 */
3197
3198 static void InitGameEngine()
3199 {
3200   int i, j, k, l, x, y;
3201
3202   /* set game engine from tape file when re-playing, else from level file */
3203   game.engine_version = (tape.playing ? tape.engine_version :
3204                          level.game_version);
3205
3206   /* ---------------------------------------------------------------------- */
3207   /* set flags for bugs and changes according to active game engine version */
3208   /* ---------------------------------------------------------------------- */
3209
3210   /*
3211     Summary of bugfix/change:
3212     Fixed handling for custom elements that change when pushed by the player.
3213
3214     Fixed/changed in version:
3215     3.1.0
3216
3217     Description:
3218     Before 3.1.0, custom elements that "change when pushing" changed directly
3219     after the player started pushing them (until then handled in "DigField()").
3220     Since 3.1.0, these custom elements are not changed until the "pushing"
3221     move of the element is finished (now handled in "ContinueMoving()").
3222
3223     Affected levels/tapes:
3224     The first condition is generally needed for all levels/tapes before version
3225     3.1.0, which might use the old behaviour before it was changed; known tapes
3226     that are affected are some tapes from the level set "Walpurgis Gardens" by
3227     Jamie Cullen.
3228     The second condition is an exception from the above case and is needed for
3229     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3230     above (including some development versions of 3.1.0), but before it was
3231     known that this change would break tapes like the above and was fixed in
3232     3.1.1, so that the changed behaviour was active although the engine version
3233     while recording maybe was before 3.1.0. There is at least one tape that is
3234     affected by this exception, which is the tape for the one-level set "Bug
3235     Machine" by Juergen Bonhagen.
3236   */
3237
3238   game.use_change_when_pushing_bug =
3239     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3240      !(tape.playing &&
3241        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3242        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3243
3244   /*
3245     Summary of bugfix/change:
3246     Fixed handling for blocking the field the player leaves when moving.
3247
3248     Fixed/changed in version:
3249     3.1.1
3250
3251     Description:
3252     Before 3.1.1, when "block last field when moving" was enabled, the field
3253     the player is leaving when moving was blocked for the time of the move,
3254     and was directly unblocked afterwards. This resulted in the last field
3255     being blocked for exactly one less than the number of frames of one player
3256     move. Additionally, even when blocking was disabled, the last field was
3257     blocked for exactly one frame.
3258     Since 3.1.1, due to changes in player movement handling, the last field
3259     is not blocked at all when blocking is disabled. When blocking is enabled,
3260     the last field is blocked for exactly the number of frames of one player
3261     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3262     last field is blocked for exactly one more than the number of frames of
3263     one player move.
3264
3265     Affected levels/tapes:
3266     (!!! yet to be determined -- probably many !!!)
3267   */
3268
3269   game.use_block_last_field_bug =
3270     (game.engine_version < VERSION_IDENT(3,1,1,0));
3271
3272   /*
3273     Summary of bugfix/change:
3274     Changed behaviour of CE changes with multiple changes per single frame.
3275
3276     Fixed/changed in version:
3277     3.2.0-6
3278
3279     Description:
3280     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3281     This resulted in race conditions where CEs seem to behave strange in some
3282     situations (where triggered CE changes were just skipped because there was
3283     already a CE change on that tile in the playfield in that engine frame).
3284     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3285     (The number of changes per frame must be limited in any case, because else
3286     it is easily possible to define CE changes that would result in an infinite
3287     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3288     should be set large enough so that it would only be reached in cases where
3289     the corresponding CE change conditions run into a loop. Therefore, it seems
3290     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3291     maximal number of change pages for custom elements.)
3292
3293     Affected levels/tapes:
3294     Probably many.
3295   */
3296
3297 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3298   game.max_num_changes_per_frame = 1;
3299 #else
3300   game.max_num_changes_per_frame =
3301     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3302 #endif
3303
3304   /* ---------------------------------------------------------------------- */
3305
3306   /* default scan direction: scan playfield from top/left to bottom/right */
3307   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3308
3309   /* dynamically adjust element properties according to game engine version */
3310   InitElementPropertiesEngine(game.engine_version);
3311
3312 #if 0
3313   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3314   printf("          tape version == %06d [%s] [file: %06d]\n",
3315          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3316          tape.file_version);
3317   printf("       => game.engine_version == %06d\n", game.engine_version);
3318 #endif
3319
3320   /* ---------- initialize player's initial move delay --------------------- */
3321
3322   /* dynamically adjust player properties according to level information */
3323   for (i = 0; i < MAX_PLAYERS; i++)
3324     game.initial_move_delay_value[i] =
3325       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3326
3327   /* dynamically adjust player properties according to game engine version */
3328   for (i = 0; i < MAX_PLAYERS; i++)
3329     game.initial_move_delay[i] =
3330       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3331        game.initial_move_delay_value[i] : 0);
3332
3333   /* ---------- initialize player's initial push delay --------------------- */
3334
3335   /* dynamically adjust player properties according to game engine version */
3336   game.initial_push_delay_value =
3337     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3338
3339   /* ---------- initialize changing elements ------------------------------- */
3340
3341   /* initialize changing elements information */
3342   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3343   {
3344     struct ElementInfo *ei = &element_info[i];
3345
3346     /* this pointer might have been changed in the level editor */
3347     ei->change = &ei->change_page[0];
3348
3349     if (!IS_CUSTOM_ELEMENT(i))
3350     {
3351       ei->change->target_element = EL_EMPTY_SPACE;
3352       ei->change->delay_fixed = 0;
3353       ei->change->delay_random = 0;
3354       ei->change->delay_frames = 1;
3355     }
3356
3357     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3358     {
3359       ei->has_change_event[j] = FALSE;
3360
3361       ei->event_page_nr[j] = 0;
3362       ei->event_page[j] = &ei->change_page[0];
3363     }
3364   }
3365
3366   /* add changing elements from pre-defined list */
3367   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3368   {
3369     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3370     struct ElementInfo *ei = &element_info[ch_delay->element];
3371
3372     ei->change->target_element       = ch_delay->target_element;
3373     ei->change->delay_fixed          = ch_delay->change_delay;
3374
3375     ei->change->pre_change_function  = ch_delay->pre_change_function;
3376     ei->change->change_function      = ch_delay->change_function;
3377     ei->change->post_change_function = ch_delay->post_change_function;
3378
3379     ei->change->can_change = TRUE;
3380     ei->change->can_change_or_has_action = TRUE;
3381
3382     ei->has_change_event[CE_DELAY] = TRUE;
3383
3384     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3385     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3386   }
3387
3388   /* ---------- initialize internal run-time variables --------------------- */
3389
3390   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3391   {
3392     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3393
3394     for (j = 0; j < ei->num_change_pages; j++)
3395     {
3396       ei->change_page[j].can_change_or_has_action =
3397         (ei->change_page[j].can_change |
3398          ei->change_page[j].has_action);
3399     }
3400   }
3401
3402   /* add change events from custom element configuration */
3403   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3404   {
3405     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3406
3407     for (j = 0; j < ei->num_change_pages; j++)
3408     {
3409       if (!ei->change_page[j].can_change_or_has_action)
3410         continue;
3411
3412       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3413       {
3414         /* only add event page for the first page found with this event */
3415         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3416         {
3417           ei->has_change_event[k] = TRUE;
3418
3419           ei->event_page_nr[k] = j;
3420           ei->event_page[k] = &ei->change_page[j];
3421         }
3422       }
3423     }
3424   }
3425
3426 #if 1
3427   /* ---------- initialize reference elements in change conditions --------- */
3428
3429   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3430   {
3431     int element = EL_CUSTOM_START + i;
3432     struct ElementInfo *ei = &element_info[element];
3433
3434     for (j = 0; j < ei->num_change_pages; j++)
3435     {
3436       int trigger_element = ei->change_page[j].initial_trigger_element;
3437
3438       if (trigger_element >= EL_PREV_CE_8 &&
3439           trigger_element <= EL_NEXT_CE_8)
3440         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3441
3442       ei->change_page[j].trigger_element = trigger_element;
3443     }
3444   }
3445 #endif
3446
3447   /* ---------- initialize run-time trigger player and element ------------- */
3448
3449   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3450   {
3451     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3452
3453     for (j = 0; j < ei->num_change_pages; j++)
3454     {
3455       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3456       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3457       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3458       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3459       ei->change_page[j].actual_trigger_ce_value = 0;
3460       ei->change_page[j].actual_trigger_ce_score = 0;
3461     }
3462   }
3463
3464   /* ---------- initialize trigger events ---------------------------------- */
3465
3466   /* initialize trigger events information */
3467   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3468     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3469       trigger_events[i][j] = FALSE;
3470
3471   /* add trigger events from element change event properties */
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473   {
3474     struct ElementInfo *ei = &element_info[i];
3475
3476     for (j = 0; j < ei->num_change_pages; j++)
3477     {
3478       if (!ei->change_page[j].can_change_or_has_action)
3479         continue;
3480
3481       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3482       {
3483         int trigger_element = ei->change_page[j].trigger_element;
3484
3485         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3486         {
3487           if (ei->change_page[j].has_event[k])
3488           {
3489             if (IS_GROUP_ELEMENT(trigger_element))
3490             {
3491               struct ElementGroupInfo *group =
3492                 element_info[trigger_element].group;
3493
3494               for (l = 0; l < group->num_elements_resolved; l++)
3495                 trigger_events[group->element_resolved[l]][k] = TRUE;
3496             }
3497             else if (trigger_element == EL_ANY_ELEMENT)
3498               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3499                 trigger_events[l][k] = TRUE;
3500             else
3501               trigger_events[trigger_element][k] = TRUE;
3502           }
3503         }
3504       }
3505     }
3506   }
3507
3508   /* ---------- initialize push delay -------------------------------------- */
3509
3510   /* initialize push delay values to default */
3511   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3512   {
3513     if (!IS_CUSTOM_ELEMENT(i))
3514     {
3515       /* set default push delay values (corrected since version 3.0.7-1) */
3516       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3517       {
3518         element_info[i].push_delay_fixed = 2;
3519         element_info[i].push_delay_random = 8;
3520       }
3521       else
3522       {
3523         element_info[i].push_delay_fixed = 8;
3524         element_info[i].push_delay_random = 8;
3525       }
3526     }
3527   }
3528
3529   /* set push delay value for certain elements from pre-defined list */
3530   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3531   {
3532     int e = push_delay_list[i].element;
3533
3534     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3535     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3536   }
3537
3538   /* set push delay value for Supaplex elements for newer engine versions */
3539   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3540   {
3541     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3542     {
3543       if (IS_SP_ELEMENT(i))
3544       {
3545         /* set SP push delay to just enough to push under a falling zonk */
3546         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3547
3548         element_info[i].push_delay_fixed  = delay;
3549         element_info[i].push_delay_random = 0;
3550       }
3551     }
3552   }
3553
3554   /* ---------- initialize move stepsize ----------------------------------- */
3555
3556   /* initialize move stepsize values to default */
3557   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558     if (!IS_CUSTOM_ELEMENT(i))
3559       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3560
3561   /* set move stepsize value for certain elements from pre-defined list */
3562   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3563   {
3564     int e = move_stepsize_list[i].element;
3565
3566     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3567   }
3568
3569   /* ---------- initialize collect score ----------------------------------- */
3570
3571   /* initialize collect score values for custom elements from initial value */
3572   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3573     if (IS_CUSTOM_ELEMENT(i))
3574       element_info[i].collect_score = element_info[i].collect_score_initial;
3575
3576   /* ---------- initialize collect count ----------------------------------- */
3577
3578   /* initialize collect count values for non-custom elements */
3579   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3580     if (!IS_CUSTOM_ELEMENT(i))
3581       element_info[i].collect_count_initial = 0;
3582
3583   /* add collect count values for all elements from pre-defined list */
3584   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3585     element_info[collect_count_list[i].element].collect_count_initial =
3586       collect_count_list[i].count;
3587
3588   /* ---------- initialize access direction -------------------------------- */
3589
3590   /* initialize access direction values to default (access from every side) */
3591   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3592     if (!IS_CUSTOM_ELEMENT(i))
3593       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3594
3595   /* set access direction value for certain elements from pre-defined list */
3596   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3597     element_info[access_direction_list[i].element].access_direction =
3598       access_direction_list[i].direction;
3599
3600   /* ---------- initialize explosion content ------------------------------- */
3601   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3602   {
3603     if (IS_CUSTOM_ELEMENT(i))
3604       continue;
3605
3606     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3607     {
3608       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3609
3610       element_info[i].content.e[x][y] =
3611         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3612          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3613          i == EL_PLAYER_3 ? EL_EMERALD :
3614          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3615          i == EL_MOLE ? EL_EMERALD_RED :
3616          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3617          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3618          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3619          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3620          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3621          i == EL_WALL_EMERALD ? EL_EMERALD :
3622          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3623          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3624          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3625          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3626          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3627          i == EL_WALL_PEARL ? EL_PEARL :
3628          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3629          EL_EMPTY);
3630     }
3631   }
3632
3633   /* ---------- initialize recursion detection ------------------------------ */
3634   recursion_loop_depth = 0;
3635   recursion_loop_detected = FALSE;
3636   recursion_loop_element = EL_UNDEFINED;
3637
3638   /* ---------- initialize graphics engine ---------------------------------- */
3639   game.scroll_delay_value =
3640     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3641      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3642   game.scroll_delay_value =
3643     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3644 }
3645
3646 int get_num_special_action(int element, int action_first, int action_last)
3647 {
3648   int num_special_action = 0;
3649   int i, j;
3650
3651   for (i = action_first; i <= action_last; i++)
3652   {
3653     boolean found = FALSE;
3654
3655     for (j = 0; j < NUM_DIRECTIONS; j++)
3656       if (el_act_dir2img(element, i, j) !=
3657           el_act_dir2img(element, ACTION_DEFAULT, j))
3658         found = TRUE;
3659
3660     if (found)
3661       num_special_action++;
3662     else
3663       break;
3664   }
3665
3666   return num_special_action;
3667 }
3668
3669
3670 /*
3671   =============================================================================
3672   InitGame()
3673   -----------------------------------------------------------------------------
3674   initialize and start new game
3675   =============================================================================
3676 */
3677
3678 void InitGame()
3679 {
3680   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3681   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3682   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3683 #if 0
3684   boolean do_fading = (game_status == GAME_MODE_MAIN);
3685 #endif
3686 #if 1
3687   int initial_move_dir = MV_DOWN;
3688 #else
3689   int initial_move_dir = MV_NONE;
3690 #endif
3691   int i, j, x, y;
3692
3693   game_status = GAME_MODE_PLAYING;
3694
3695   InitGameEngine();
3696   InitGameControlValues();
3697
3698   /* don't play tapes over network */
3699   network_playing = (options.network && !tape.playing);
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     struct PlayerInfo *player = &stored_player[i];
3704
3705     player->index_nr = i;
3706     player->index_bit = (1 << i);
3707     player->element_nr = EL_PLAYER_1 + i;
3708
3709     player->present = FALSE;
3710     player->active = FALSE;
3711     player->killed = FALSE;
3712     player->reanimated = FALSE;
3713
3714     player->action = 0;
3715     player->effective_action = 0;
3716     player->programmed_action = 0;
3717
3718     player->score = 0;
3719     player->score_final = 0;
3720
3721     player->gems_still_needed = level.gems_needed;
3722     player->sokobanfields_still_needed = 0;
3723     player->lights_still_needed = 0;
3724     player->friends_still_needed = 0;
3725
3726     for (j = 0; j < MAX_NUM_KEYS; j++)
3727       player->key[j] = FALSE;
3728
3729     player->num_white_keys = 0;
3730
3731     player->dynabomb_count = 0;
3732     player->dynabomb_size = 1;
3733     player->dynabombs_left = 0;
3734     player->dynabomb_xl = FALSE;
3735
3736     player->MovDir = initial_move_dir;
3737     player->MovPos = 0;
3738     player->GfxPos = 0;
3739     player->GfxDir = initial_move_dir;
3740     player->GfxAction = ACTION_DEFAULT;
3741     player->Frame = 0;
3742     player->StepFrame = 0;
3743
3744     player->initial_element = player->element_nr;
3745     player->artwork_element =
3746       (level.use_artwork_element[i] ? level.artwork_element[i] :
3747        player->element_nr);
3748     player->use_murphy = FALSE;
3749
3750     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3751     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3752
3753     player->gravity = level.initial_player_gravity[i];
3754
3755     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3756
3757     player->actual_frame_counter = 0;
3758
3759     player->step_counter = 0;
3760
3761     player->last_move_dir = initial_move_dir;
3762
3763     player->is_active = FALSE;
3764
3765     player->is_waiting = FALSE;
3766     player->is_moving = FALSE;
3767     player->is_auto_moving = FALSE;
3768     player->is_digging = FALSE;
3769     player->is_snapping = FALSE;
3770     player->is_collecting = FALSE;
3771     player->is_pushing = FALSE;
3772     player->is_switching = FALSE;
3773     player->is_dropping = FALSE;
3774     player->is_dropping_pressed = FALSE;
3775
3776     player->is_bored = FALSE;
3777     player->is_sleeping = FALSE;
3778
3779     player->frame_counter_bored = -1;
3780     player->frame_counter_sleeping = -1;
3781
3782     player->anim_delay_counter = 0;
3783     player->post_delay_counter = 0;
3784
3785     player->dir_waiting = initial_move_dir;
3786     player->action_waiting = ACTION_DEFAULT;
3787     player->last_action_waiting = ACTION_DEFAULT;
3788     player->special_action_bored = ACTION_DEFAULT;
3789     player->special_action_sleeping = ACTION_DEFAULT;
3790
3791     player->switch_x = -1;
3792     player->switch_y = -1;
3793
3794     player->drop_x = -1;
3795     player->drop_y = -1;
3796
3797     player->show_envelope = 0;
3798
3799     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3800
3801     player->push_delay       = -1;      /* initialized when pushing starts */
3802     player->push_delay_value = game.initial_push_delay_value;
3803
3804     player->drop_delay = 0;
3805     player->drop_pressed_delay = 0;
3806
3807     player->last_jx = -1;
3808     player->last_jy = -1;
3809     player->jx = -1;
3810     player->jy = -1;
3811
3812     player->shield_normal_time_left = 0;
3813     player->shield_deadly_time_left = 0;
3814
3815     player->inventory_infinite_element = EL_UNDEFINED;
3816     player->inventory_size = 0;
3817
3818     if (level.use_initial_inventory[i])
3819     {
3820       for (j = 0; j < level.initial_inventory_size[i]; j++)
3821       {
3822         int element = level.initial_inventory_content[i][j];
3823         int collect_count = element_info[element].collect_count_initial;
3824         int k;
3825
3826         if (!IS_CUSTOM_ELEMENT(element))
3827           collect_count = 1;
3828
3829         if (collect_count == 0)
3830           player->inventory_infinite_element = element;
3831         else
3832           for (k = 0; k < collect_count; k++)
3833             if (player->inventory_size < MAX_INVENTORY_SIZE)
3834               player->inventory_element[player->inventory_size++] = element;
3835       }
3836     }
3837
3838     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3839     SnapField(player, 0, 0);
3840
3841     player->LevelSolved = FALSE;
3842     player->GameOver = FALSE;
3843
3844     player->LevelSolved_GameWon = FALSE;
3845     player->LevelSolved_GameEnd = FALSE;
3846     player->LevelSolved_PanelOff = FALSE;
3847     player->LevelSolved_SaveTape = FALSE;
3848     player->LevelSolved_SaveScore = FALSE;
3849     player->LevelSolved_CountingTime = 0;
3850     player->LevelSolved_CountingScore = 0;
3851   }
3852
3853   network_player_action_received = FALSE;
3854
3855 #if defined(NETWORK_AVALIABLE)
3856   /* initial null action */
3857   if (network_playing)
3858     SendToServer_MovePlayer(MV_NONE);
3859 #endif
3860
3861   ZX = ZY = -1;
3862   ExitX = ExitY = -1;
3863
3864   FrameCounter = 0;
3865   TimeFrames = 0;
3866   TimePlayed = 0;
3867   TimeLeft = level.time;
3868   TapeTime = 0;
3869
3870   ScreenMovDir = MV_NONE;
3871   ScreenMovPos = 0;
3872   ScreenGfxPos = 0;
3873
3874   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3875
3876   AllPlayersGone = FALSE;
3877
3878   game.yamyam_content_nr = 0;
3879   game.robot_wheel_active = FALSE;
3880   game.magic_wall_active = FALSE;
3881   game.magic_wall_time_left = 0;
3882   game.light_time_left = 0;
3883   game.timegate_time_left = 0;
3884   game.switchgate_pos = 0;
3885   game.wind_direction = level.wind_direction_initial;
3886
3887 #if !USE_PLAYER_GRAVITY
3888   game.gravity = FALSE;
3889   game.explosions_delayed = TRUE;
3890 #endif
3891
3892   game.lenses_time_left = 0;
3893   game.magnify_time_left = 0;
3894
3895   game.ball_state = level.ball_state_initial;
3896   game.ball_content_nr = 0;
3897
3898   game.envelope_active = FALSE;
3899
3900   /* set focus to local player for network games, else to all players */
3901   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3902   game.centered_player_nr_next = game.centered_player_nr;
3903   game.set_centered_player = FALSE;
3904
3905   if (network_playing && tape.recording)
3906   {
3907     /* store client dependent player focus when recording network games */
3908     tape.centered_player_nr_next = game.centered_player_nr_next;
3909     tape.set_centered_player = TRUE;
3910   }
3911
3912   for (i = 0; i < NUM_BELTS; i++)
3913   {
3914     game.belt_dir[i] = MV_NONE;
3915     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3916   }
3917
3918   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3919     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3920
3921   SCAN_PLAYFIELD(x, y)
3922   {
3923     Feld[x][y] = level.field[x][y];
3924     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3925     ChangeDelay[x][y] = 0;
3926     ChangePage[x][y] = -1;
3927 #if USE_NEW_CUSTOM_VALUE
3928     CustomValue[x][y] = 0;              /* initialized in InitField() */
3929 #endif
3930     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3931     AmoebaNr[x][y] = 0;
3932     WasJustMoving[x][y] = 0;
3933     WasJustFalling[x][y] = 0;
3934     CheckCollision[x][y] = 0;
3935     CheckImpact[x][y] = 0;
3936     Stop[x][y] = FALSE;
3937     Pushed[x][y] = FALSE;
3938
3939     ChangeCount[x][y] = 0;
3940     ChangeEvent[x][y] = -1;
3941
3942     ExplodePhase[x][y] = 0;
3943     ExplodeDelay[x][y] = 0;
3944     ExplodeField[x][y] = EX_TYPE_NONE;
3945
3946     RunnerVisit[x][y] = 0;
3947     PlayerVisit[x][y] = 0;
3948
3949     GfxFrame[x][y] = 0;
3950     GfxRandom[x][y] = INIT_GFX_RANDOM();
3951     GfxElement[x][y] = EL_UNDEFINED;
3952     GfxAction[x][y] = ACTION_DEFAULT;
3953     GfxDir[x][y] = MV_NONE;
3954     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3955   }
3956
3957   SCAN_PLAYFIELD(x, y)
3958   {
3959     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3960       emulate_bd = FALSE;
3961     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3962       emulate_sb = FALSE;
3963     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3964       emulate_sp = FALSE;
3965
3966     InitField(x, y, TRUE);
3967
3968     ResetGfxAnimation(x, y);
3969   }
3970
3971   InitBeltMovement();
3972
3973   for (i = 0; i < MAX_PLAYERS; i++)
3974   {
3975     struct PlayerInfo *player = &stored_player[i];
3976
3977     /* set number of special actions for bored and sleeping animation */
3978     player->num_special_action_bored =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_BORING_1, ACTION_BORING_LAST);
3981     player->num_special_action_sleeping =
3982       get_num_special_action(player->artwork_element,
3983                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3984   }
3985
3986   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3987                     emulate_sb ? EMU_SOKOBAN :
3988                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3989
3990 #if USE_NEW_ALL_SLIPPERY
3991   /* initialize type of slippery elements */
3992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3993   {
3994     if (!IS_CUSTOM_ELEMENT(i))
3995     {
3996       /* default: elements slip down either to the left or right randomly */
3997       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3998
3999       /* SP style elements prefer to slip down on the left side */
4000       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4001         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4002
4003       /* BD style elements prefer to slip down on the left side */
4004       if (game.emulation == EMU_BOULDERDASH)
4005         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4006     }
4007   }
4008 #endif
4009
4010   /* initialize explosion and ignition delay */
4011   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4012   {
4013     if (!IS_CUSTOM_ELEMENT(i))
4014     {
4015       int num_phase = 8;
4016       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4017                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4018                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4019       int last_phase = (num_phase + 1) * delay;
4020       int half_phase = (num_phase / 2) * delay;
4021
4022       element_info[i].explosion_delay = last_phase - 1;
4023       element_info[i].ignition_delay = half_phase;
4024
4025       if (i == EL_BLACK_ORB)
4026         element_info[i].ignition_delay = 1;
4027     }
4028
4029 #if 0
4030     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4031       element_info[i].explosion_delay = 1;
4032
4033     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4034       element_info[i].ignition_delay = 1;
4035 #endif
4036   }
4037
4038   /* correct non-moving belts to start moving left */
4039   for (i = 0; i < NUM_BELTS; i++)
4040     if (game.belt_dir[i] == MV_NONE)
4041       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4042
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044   /* check if any connected player was not found in playfield */
4045   for (i = 0; i < MAX_PLAYERS; i++)
4046   {
4047     struct PlayerInfo *player = &stored_player[i];
4048
4049     if (player->connected && !player->present)
4050     {
4051       for (j = 0; j < MAX_PLAYERS; j++)
4052       {
4053         struct PlayerInfo *some_player = &stored_player[j];
4054         int jx = some_player->jx, jy = some_player->jy;
4055
4056         /* assign first free player found that is present in the playfield */
4057         if (some_player->present && !some_player->connected)
4058         {
4059           player->present = FALSE;
4060           player->active = FALSE;
4061
4062           some_player->present = TRUE;
4063           some_player->active = TRUE;
4064
4065           /*
4066           player->initial_element = some_player->initial_element;
4067           player->artwork_element = some_player->artwork_element;
4068
4069           player->block_last_field       = some_player->block_last_field;
4070           player->block_delay_adjustment = some_player->block_delay_adjustment;
4071           */
4072
4073           StorePlayer[jx][jy] = some_player->element_nr;
4074
4075           some_player->jx = some_player->last_jx = jx;
4076           some_player->jy = some_player->last_jy = jy;
4077
4078           if (local_player == player)
4079             local_player = some_player;
4080
4081           break;
4082         }
4083       }
4084     }
4085   }
4086
4087 #else
4088
4089   /* check if any connected player was not found in playfield */
4090   for (i = 0; i < MAX_PLAYERS; i++)
4091   {
4092     struct PlayerInfo *player = &stored_player[i];
4093
4094     if (player->connected && !player->present)
4095     {
4096       for (j = 0; j < MAX_PLAYERS; j++)
4097       {
4098         struct PlayerInfo *some_player = &stored_player[j];
4099         int jx = some_player->jx, jy = some_player->jy;
4100
4101         /* assign first free player found that is present in the playfield */
4102         if (some_player->present && !some_player->connected)
4103         {
4104           player->present = TRUE;
4105           player->active = TRUE;
4106
4107           some_player->present = FALSE;
4108           some_player->active = FALSE;
4109
4110           player->initial_element = some_player->initial_element;
4111           player->artwork_element = some_player->artwork_element;
4112
4113           player->block_last_field       = some_player->block_last_field;
4114           player->block_delay_adjustment = some_player->block_delay_adjustment;
4115
4116           StorePlayer[jx][jy] = player->element_nr;
4117
4118           player->jx = player->last_jx = jx;
4119           player->jy = player->last_jy = jy;
4120
4121           break;
4122         }
4123       }
4124     }
4125   }
4126 #endif
4127
4128   if (tape.playing)
4129   {
4130     /* when playing a tape, eliminate all players who do not participate */
4131
4132     for (i = 0; i < MAX_PLAYERS; i++)
4133     {
4134       if (stored_player[i].active && !tape.player_participates[i])
4135       {
4136         struct PlayerInfo *player = &stored_player[i];
4137         int jx = player->jx, jy = player->jy;
4138
4139         player->active = FALSE;
4140         StorePlayer[jx][jy] = 0;
4141         Feld[jx][jy] = EL_EMPTY;
4142       }
4143     }
4144   }
4145   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4146   {
4147     /* when in single player mode, eliminate all but the first active player */
4148
4149     for (i = 0; i < MAX_PLAYERS; i++)
4150     {
4151       if (stored_player[i].active)
4152       {
4153         for (j = i + 1; j < MAX_PLAYERS; j++)
4154         {
4155           if (stored_player[j].active)
4156           {
4157             struct PlayerInfo *player = &stored_player[j];
4158             int jx = player->jx, jy = player->jy;
4159
4160             player->active = FALSE;
4161             player->present = FALSE;
4162
4163             StorePlayer[jx][jy] = 0;
4164             Feld[jx][jy] = EL_EMPTY;
4165           }
4166         }
4167       }
4168     }
4169   }
4170
4171   /* when recording the game, store which players take part in the game */
4172   if (tape.recording)
4173   {
4174     for (i = 0; i < MAX_PLAYERS; i++)
4175       if (stored_player[i].active)
4176         tape.player_participates[i] = TRUE;
4177   }
4178
4179   if (options.debug)
4180   {
4181     for (i = 0; i < MAX_PLAYERS; i++)
4182     {
4183       struct PlayerInfo *player = &stored_player[i];
4184
4185       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4186              i+1,
4187              player->present,
4188              player->connected,
4189              player->active);
4190       if (local_player == player)
4191         printf("Player  %d is local player.\n", i+1);
4192     }
4193   }
4194
4195   if (BorderElement == EL_EMPTY)
4196   {
4197     SBX_Left = 0;
4198     SBX_Right = lev_fieldx - SCR_FIELDX;
4199     SBY_Upper = 0;
4200     SBY_Lower = lev_fieldy - SCR_FIELDY;
4201   }
4202   else
4203   {
4204     SBX_Left = -1;
4205     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4206     SBY_Upper = -1;
4207     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4208   }
4209
4210   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4211     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4212
4213   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4214     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4215
4216   /* if local player not found, look for custom element that might create
4217      the player (make some assumptions about the right custom element) */
4218   if (!local_player->present)
4219   {
4220     int start_x = 0, start_y = 0;
4221     int found_rating = 0;
4222     int found_element = EL_UNDEFINED;
4223     int player_nr = local_player->index_nr;
4224
4225     SCAN_PLAYFIELD(x, y)
4226     {
4227       int element = Feld[x][y];
4228       int content;
4229       int xx, yy;
4230       boolean is_player;
4231
4232       if (level.use_start_element[player_nr] &&
4233           level.start_element[player_nr] == element &&
4234           found_rating < 4)
4235       {
4236         start_x = x;
4237         start_y = y;
4238
4239         found_rating = 4;
4240         found_element = element;
4241       }
4242
4243       if (!IS_CUSTOM_ELEMENT(element))
4244         continue;
4245
4246       if (CAN_CHANGE(element))
4247       {
4248         for (i = 0; i < element_info[element].num_change_pages; i++)
4249         {
4250           /* check for player created from custom element as single target */
4251           content = element_info[element].change_page[i].target_element;
4252           is_player = ELEM_IS_PLAYER(content);
4253
4254           if (is_player && (found_rating < 3 ||
4255                             (found_rating == 3 && element < found_element)))
4256           {
4257             start_x = x;
4258             start_y = y;
4259
4260             found_rating = 3;
4261             found_element = element;
4262           }
4263         }
4264       }
4265
4266       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4267       {
4268         /* check for player created from custom element as explosion content */
4269         content = element_info[element].content.e[xx][yy];
4270         is_player = ELEM_IS_PLAYER(content);
4271
4272         if (is_player && (found_rating < 2 ||
4273                           (found_rating == 2 && element < found_element)))
4274         {
4275           start_x = x + xx - 1;
4276           start_y = y + yy - 1;
4277
4278           found_rating = 2;
4279           found_element = element;
4280         }
4281
4282         if (!CAN_CHANGE(element))
4283           continue;
4284
4285         for (i = 0; i < element_info[element].num_change_pages; i++)
4286         {
4287           /* check for player created from custom element as extended target */
4288           content =
4289             element_info[element].change_page[i].target_content.e[xx][yy];
4290
4291           is_player = ELEM_IS_PLAYER(content);
4292
4293           if (is_player && (found_rating < 1 ||
4294                             (found_rating == 1 && element < found_element)))
4295           {
4296             start_x = x + xx - 1;
4297             start_y = y + yy - 1;
4298
4299             found_rating = 1;
4300             found_element = element;
4301           }
4302         }
4303       }
4304     }
4305
4306     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4307                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4308                 start_x - MIDPOSX);
4309
4310     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4311                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4312                 start_y - MIDPOSY);
4313   }
4314   else
4315   {
4316     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4317                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4318                 local_player->jx - MIDPOSX);
4319
4320     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4321                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4322                 local_player->jy - MIDPOSY);
4323   }
4324
4325 #if 0
4326   /* do not use PLAYING mask for fading out from main screen */
4327   game_status = GAME_MODE_MAIN;
4328 #endif
4329
4330   StopAnimation();
4331
4332   if (!game.restart_level)
4333     CloseDoor(DOOR_CLOSE_1);
4334
4335 #if 1
4336   if (level_editor_test_game)
4337     FadeSkipNextFadeIn();
4338   else
4339     FadeSetEnterScreen();
4340 #else
4341   if (level_editor_test_game)
4342     fading = fading_none;
4343   else
4344     fading = menu.destination;
4345 #endif
4346
4347 #if 1
4348   FadeOut(REDRAW_FIELD);
4349 #else
4350   if (do_fading)
4351     FadeOut(REDRAW_FIELD);
4352 #endif
4353
4354 #if 0
4355   game_status = GAME_MODE_PLAYING;
4356 #endif
4357
4358   /* !!! FIX THIS (START) !!! */
4359   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4360   {
4361     InitGameEngine_EM();
4362
4363     /* blit playfield from scroll buffer to normal back buffer for fading in */
4364     BlitScreenToBitmap_EM(backbuffer);
4365   }
4366   else
4367   {
4368     DrawLevel();
4369     DrawAllPlayers();
4370
4371     /* after drawing the level, correct some elements */
4372     if (game.timegate_time_left == 0)
4373       CloseAllOpenTimegates();
4374
4375     /* blit playfield from scroll buffer to normal back buffer for fading in */
4376     if (setup.soft_scrolling)
4377       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4378
4379     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4380   }
4381   /* !!! FIX THIS (END) !!! */
4382
4383 #if 1
4384   FadeIn(REDRAW_FIELD);
4385 #else
4386   if (do_fading)
4387     FadeIn(REDRAW_FIELD);
4388
4389   BackToFront();
4390 #endif
4391
4392   if (!game.restart_level)
4393   {
4394     /* copy default game door content to main double buffer */
4395     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4396                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4397   }
4398
4399   SetPanelBackground();
4400   SetDrawBackgroundMask(REDRAW_DOOR_1);
4401
4402 #if 1
4403   UpdateAndDisplayGameControlValues();
4404 #else
4405   UpdateGameDoorValues();
4406   DrawGameDoorValues();
4407 #endif
4408
4409   if (!game.restart_level)
4410   {
4411     UnmapGameButtons();
4412     UnmapTapeButtons();
4413     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4414     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4415     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4416     MapGameButtons();
4417     MapTapeButtons();
4418
4419     /* copy actual game door content to door double buffer for OpenDoor() */
4420     BlitBitmap(drawto, bitmap_db_door,
4421                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4422
4423     OpenDoor(DOOR_OPEN_ALL);
4424
4425     PlaySound(SND_GAME_STARTING);
4426
4427     if (setup.sound_music)
4428       PlayLevelMusic();
4429
4430     KeyboardAutoRepeatOffUnlessAutoplay();
4431
4432     if (options.debug)
4433     {
4434       for (i = 0; i < MAX_PLAYERS; i++)
4435         printf("Player %d %sactive.\n",
4436                i + 1, (stored_player[i].active ? "" : "not "));
4437     }
4438   }
4439
4440 #if 1
4441   UnmapAllGadgets();
4442
4443   MapGameButtons();
4444   MapTapeButtons();
4445 #endif
4446
4447   game.restart_level = FALSE;
4448 }
4449
4450 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4451 {
4452   /* this is used for non-R'n'D game engines to update certain engine values */
4453
4454   /* needed to determine if sounds are played within the visible screen area */
4455   scroll_x = actual_scroll_x;
4456   scroll_y = actual_scroll_y;
4457 }
4458
4459 void InitMovDir(int x, int y)
4460 {
4461   int i, element = Feld[x][y];
4462   static int xy[4][2] =
4463   {
4464     {  0, +1 },
4465     { +1,  0 },
4466     {  0, -1 },
4467     { -1,  0 }
4468   };
4469   static int direction[3][4] =
4470   {
4471     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4472     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4473     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4474   };
4475
4476   switch (element)
4477   {
4478     case EL_BUG_RIGHT:
4479     case EL_BUG_UP:
4480     case EL_BUG_LEFT:
4481     case EL_BUG_DOWN:
4482       Feld[x][y] = EL_BUG;
4483       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4484       break;
4485
4486     case EL_SPACESHIP_RIGHT:
4487     case EL_SPACESHIP_UP:
4488     case EL_SPACESHIP_LEFT:
4489     case EL_SPACESHIP_DOWN:
4490       Feld[x][y] = EL_SPACESHIP;
4491       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4492       break;
4493
4494     case EL_BD_BUTTERFLY_RIGHT:
4495     case EL_BD_BUTTERFLY_UP:
4496     case EL_BD_BUTTERFLY_LEFT:
4497     case EL_BD_BUTTERFLY_DOWN:
4498       Feld[x][y] = EL_BD_BUTTERFLY;
4499       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4500       break;
4501
4502     case EL_BD_FIREFLY_RIGHT:
4503     case EL_BD_FIREFLY_UP:
4504     case EL_BD_FIREFLY_LEFT:
4505     case EL_BD_FIREFLY_DOWN:
4506       Feld[x][y] = EL_BD_FIREFLY;
4507       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4508       break;
4509
4510     case EL_PACMAN_RIGHT:
4511     case EL_PACMAN_UP:
4512     case EL_PACMAN_LEFT:
4513     case EL_PACMAN_DOWN:
4514       Feld[x][y] = EL_PACMAN;
4515       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4516       break;
4517
4518     case EL_YAMYAM_LEFT:
4519     case EL_YAMYAM_RIGHT:
4520     case EL_YAMYAM_UP:
4521     case EL_YAMYAM_DOWN:
4522       Feld[x][y] = EL_YAMYAM;
4523       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4524       break;
4525
4526     case EL_SP_SNIKSNAK:
4527       MovDir[x][y] = MV_UP;
4528       break;
4529
4530     case EL_SP_ELECTRON:
4531       MovDir[x][y] = MV_LEFT;
4532       break;
4533
4534     case EL_MOLE_LEFT:
4535     case EL_MOLE_RIGHT:
4536     case EL_MOLE_UP:
4537     case EL_MOLE_DOWN:
4538       Feld[x][y] = EL_MOLE;
4539       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4540       break;
4541
4542     default:
4543       if (IS_CUSTOM_ELEMENT(element))
4544       {
4545         struct ElementInfo *ei = &element_info[element];
4546         int move_direction_initial = ei->move_direction_initial;
4547         int move_pattern = ei->move_pattern;
4548
4549         if (move_direction_initial == MV_START_PREVIOUS)
4550         {
4551           if (MovDir[x][y] != MV_NONE)
4552             return;
4553
4554           move_direction_initial = MV_START_AUTOMATIC;
4555         }
4556
4557         if (move_direction_initial == MV_START_RANDOM)
4558           MovDir[x][y] = 1 << RND(4);
4559         else if (move_direction_initial & MV_ANY_DIRECTION)
4560           MovDir[x][y] = move_direction_initial;
4561         else if (move_pattern == MV_ALL_DIRECTIONS ||
4562                  move_pattern == MV_TURNING_LEFT ||
4563                  move_pattern == MV_TURNING_RIGHT ||
4564                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4565                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4566                  move_pattern == MV_TURNING_RANDOM)
4567           MovDir[x][y] = 1 << RND(4);
4568         else if (move_pattern == MV_HORIZONTAL)
4569           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4570         else if (move_pattern == MV_VERTICAL)
4571           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4572         else if (move_pattern & MV_ANY_DIRECTION)
4573           MovDir[x][y] = element_info[element].move_pattern;
4574         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4575                  move_pattern == MV_ALONG_RIGHT_SIDE)
4576         {
4577           /* use random direction as default start direction */
4578           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4579             MovDir[x][y] = 1 << RND(4);
4580
4581           for (i = 0; i < NUM_DIRECTIONS; i++)
4582           {
4583             int x1 = x + xy[i][0];
4584             int y1 = y + xy[i][1];
4585
4586             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4587             {
4588               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4589                 MovDir[x][y] = direction[0][i];
4590               else
4591                 MovDir[x][y] = direction[1][i];
4592
4593               break;
4594             }
4595           }
4596         }                
4597       }
4598       else
4599       {
4600         MovDir[x][y] = 1 << RND(4);
4601
4602         if (element != EL_BUG &&
4603             element != EL_SPACESHIP &&
4604             element != EL_BD_BUTTERFLY &&
4605             element != EL_BD_FIREFLY)
4606           break;
4607
4608         for (i = 0; i < NUM_DIRECTIONS; i++)
4609         {
4610           int x1 = x + xy[i][0];
4611           int y1 = y + xy[i][1];
4612
4613           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4614           {
4615             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4616             {
4617               MovDir[x][y] = direction[0][i];
4618               break;
4619             }
4620             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4621                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4622             {
4623               MovDir[x][y] = direction[1][i];
4624               break;
4625             }
4626           }
4627         }
4628       }
4629       break;
4630   }
4631
4632   GfxDir[x][y] = MovDir[x][y];
4633 }
4634
4635 void InitAmoebaNr(int x, int y)
4636 {
4637   int i;
4638   int group_nr = AmoebeNachbarNr(x, y);
4639
4640   if (group_nr == 0)
4641   {
4642     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4643     {
4644       if (AmoebaCnt[i] == 0)
4645       {
4646         group_nr = i;
4647         break;
4648       }
4649     }
4650   }
4651
4652   AmoebaNr[x][y] = group_nr;
4653   AmoebaCnt[group_nr]++;
4654   AmoebaCnt2[group_nr]++;
4655 }
4656
4657 static void PlayerWins(struct PlayerInfo *player)
4658 {
4659   player->LevelSolved = TRUE;
4660   player->GameOver = TRUE;
4661
4662   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4663                          level.native_em_level->lev->score : player->score);
4664
4665   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4666   player->LevelSolved_CountingScore = player->score_final;
4667 }
4668
4669 void GameWon()
4670 {
4671   static int time, time_final;
4672   static int score, score_final;
4673   static int game_over_delay_1 = 0;
4674   static int game_over_delay_2 = 0;
4675   int game_over_delay_value_1 = 50;
4676   int game_over_delay_value_2 = 50;
4677
4678   if (!local_player->LevelSolved_GameWon)
4679   {
4680     int i;
4681
4682     /* do not start end game actions before the player stops moving (to exit) */
4683     if (local_player->MovPos)
4684       return;
4685
4686     local_player->LevelSolved_GameWon = TRUE;
4687     local_player->LevelSolved_SaveTape = tape.recording;
4688     local_player->LevelSolved_SaveScore = !tape.playing;
4689
4690     if (tape.auto_play)         /* tape might already be stopped here */
4691       tape.auto_play_level_solved = TRUE;
4692
4693 #if 1
4694     TapeStop();
4695 #endif
4696
4697     game_over_delay_1 = game_over_delay_value_1;
4698     game_over_delay_2 = game_over_delay_value_2;
4699
4700     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4701     score = score_final = local_player->score_final;
4702
4703     if (TimeLeft > 0)
4704     {
4705       time_final = 0;
4706       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4707     }
4708     else if (level.time == 0 && TimePlayed < 999)
4709     {
4710       time_final = 999;
4711       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4712     }
4713
4714     local_player->score_final = score_final;
4715
4716     if (level_editor_test_game)
4717     {
4718       time = time_final;
4719       score = score_final;
4720
4721 #if 1
4722       local_player->LevelSolved_CountingTime = time;
4723       local_player->LevelSolved_CountingScore = score;
4724
4725       game_panel_controls[GAME_PANEL_TIME].value = time;
4726       game_panel_controls[GAME_PANEL_SCORE].value = score;
4727
4728       DisplayGameControlValues();
4729 #else
4730       DrawGameValue_Time(time);
4731       DrawGameValue_Score(score);
4732 #endif
4733     }
4734
4735     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4736     {
4737       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4738       {
4739         /* close exit door after last player */
4740         if ((AllPlayersGone &&
4741              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4742               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4743               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4744             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4745             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4746         {
4747           int element = Feld[ExitX][ExitY];
4748
4749 #if 0
4750           if (element == EL_EM_EXIT_OPEN ||
4751               element == EL_EM_STEEL_EXIT_OPEN)
4752           {
4753             Bang(ExitX, ExitY);
4754           }
4755           else
4756 #endif
4757           {
4758             Feld[ExitX][ExitY] =
4759               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4760                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4761                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4762                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4763                EL_EM_STEEL_EXIT_CLOSING);
4764
4765             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4766           }
4767         }
4768
4769         /* player disappears */
4770         DrawLevelField(ExitX, ExitY);
4771       }
4772
4773       for (i = 0; i < MAX_PLAYERS; i++)
4774       {
4775         struct PlayerInfo *player = &stored_player[i];
4776
4777         if (player->present)
4778         {
4779           RemovePlayer(player);
4780
4781           /* player disappears */
4782           DrawLevelField(player->jx, player->jy);
4783         }
4784       }
4785     }
4786
4787     PlaySound(SND_GAME_WINNING);
4788   }
4789
4790   if (game_over_delay_1 > 0)
4791   {
4792     game_over_delay_1--;
4793
4794     return;
4795   }
4796
4797   if (time != time_final)
4798   {
4799     int time_to_go = ABS(time_final - time);
4800     int time_count_dir = (time < time_final ? +1 : -1);
4801     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4802
4803     time  += time_count_steps * time_count_dir;
4804     score += time_count_steps * level.score[SC_TIME_BONUS];
4805
4806 #if 1
4807     local_player->LevelSolved_CountingTime = time;
4808     local_player->LevelSolved_CountingScore = score;
4809
4810     game_panel_controls[GAME_PANEL_TIME].value = time;
4811     game_panel_controls[GAME_PANEL_SCORE].value = score;
4812
4813     DisplayGameControlValues();
4814 #else
4815     DrawGameValue_Time(time);
4816     DrawGameValue_Score(score);
4817 #endif
4818
4819     if (time == time_final)
4820       StopSound(SND_GAME_LEVELTIME_BONUS);
4821     else if (setup.sound_loops)
4822       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4823     else
4824       PlaySound(SND_GAME_LEVELTIME_BONUS);
4825
4826     return;
4827   }
4828
4829   local_player->LevelSolved_PanelOff = TRUE;
4830
4831   if (game_over_delay_2 > 0)
4832   {
4833     game_over_delay_2--;
4834
4835     return;
4836   }
4837
4838 #if 1
4839   GameEnd();
4840 #endif
4841 }
4842
4843 void GameEnd()
4844 {
4845   int hi_pos;
4846   boolean raise_level = FALSE;
4847
4848   local_player->LevelSolved_GameEnd = TRUE;
4849
4850   CloseDoor(DOOR_CLOSE_1);
4851
4852   if (local_player->LevelSolved_SaveTape)
4853   {
4854 #if 0
4855     TapeStop();
4856 #endif
4857
4858 #if 1
4859     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4860 #else
4861     SaveTape(tape.level_nr);            /* ask to save tape */
4862 #endif
4863   }
4864
4865   if (level_editor_test_game)
4866   {
4867     game_status = GAME_MODE_MAIN;
4868
4869 #if 1
4870     DrawAndFadeInMainMenu(REDRAW_FIELD);
4871 #else
4872     DrawMainMenu();
4873 #endif
4874
4875     return;
4876   }
4877
4878   if (!local_player->LevelSolved_SaveScore)
4879   {
4880 #if 1
4881     FadeOut(REDRAW_FIELD);
4882 #endif
4883
4884     game_status = GAME_MODE_MAIN;
4885
4886     DrawAndFadeInMainMenu(REDRAW_FIELD);
4887
4888     return;
4889   }
4890
4891   if (level_nr == leveldir_current->handicap_level)
4892   {
4893     leveldir_current->handicap_level++;
4894     SaveLevelSetup_SeriesInfo();
4895   }
4896
4897   if (level_nr < leveldir_current->last_level)
4898     raise_level = TRUE;                 /* advance to next level */
4899
4900   if ((hi_pos = NewHiScore()) >= 0) 
4901   {
4902     game_status = GAME_MODE_SCORES;
4903
4904     DrawHallOfFame(hi_pos);
4905
4906     if (raise_level)
4907     {
4908       level_nr++;
4909       TapeErase();
4910     }
4911   }
4912   else
4913   {
4914 #if 1
4915     FadeOut(REDRAW_FIELD);
4916 #endif
4917
4918     game_status = GAME_MODE_MAIN;
4919
4920     if (raise_level)
4921     {
4922       level_nr++;
4923       TapeErase();
4924     }
4925
4926     DrawAndFadeInMainMenu(REDRAW_FIELD);
4927   }
4928 }
4929
4930 int NewHiScore()
4931 {
4932   int k, l;
4933   int position = -1;
4934
4935   LoadScore(level_nr);
4936
4937   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4938       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4939     return -1;
4940
4941   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4942   {
4943     if (local_player->score_final > highscore[k].Score)
4944     {
4945       /* player has made it to the hall of fame */
4946
4947       if (k < MAX_SCORE_ENTRIES - 1)
4948       {
4949         int m = MAX_SCORE_ENTRIES - 1;
4950
4951 #ifdef ONE_PER_NAME
4952         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4953           if (strEqual(setup.player_name, highscore[l].Name))
4954             m = l;
4955         if (m == k)     /* player's new highscore overwrites his old one */
4956           goto put_into_list;
4957 #endif
4958
4959         for (l = m; l > k; l--)
4960         {
4961           strcpy(highscore[l].Name, highscore[l - 1].Name);
4962           highscore[l].Score = highscore[l - 1].Score;
4963         }
4964       }
4965
4966 #ifdef ONE_PER_NAME
4967       put_into_list:
4968 #endif
4969       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4970       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4971       highscore[k].Score = local_player->score_final; 
4972       position = k;
4973       break;
4974     }
4975
4976 #ifdef ONE_PER_NAME
4977     else if (!strncmp(setup.player_name, highscore[k].Name,
4978                       MAX_PLAYER_NAME_LEN))
4979       break;    /* player already there with a higher score */
4980 #endif
4981
4982   }
4983
4984   if (position >= 0) 
4985     SaveScore(level_nr);
4986
4987   return position;
4988 }
4989
4990 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4991 {
4992   int element = Feld[x][y];
4993   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4994   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4995   int horiz_move = (dx != 0);
4996   int sign = (horiz_move ? dx : dy);
4997   int step = sign * element_info[element].move_stepsize;
4998
4999   /* special values for move stepsize for spring and things on conveyor belt */
5000   if (horiz_move)
5001   {
5002     if (CAN_FALL(element) &&
5003         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5004       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5005     else if (element == EL_SPRING)
5006       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5007   }
5008
5009   return step;
5010 }
5011
5012 inline static int getElementMoveStepsize(int x, int y)
5013 {
5014   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5015 }
5016
5017 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5018 {
5019   if (player->GfxAction != action || player->GfxDir != dir)
5020   {
5021 #if 0
5022     printf("Player frame reset! (%d => %d, %d => %d)\n",
5023            player->GfxAction, action, player->GfxDir, dir);
5024 #endif
5025
5026     player->GfxAction = action;
5027     player->GfxDir = dir;
5028     player->Frame = 0;
5029     player->StepFrame = 0;
5030   }
5031 }
5032
5033 #if USE_GFX_RESET_GFX_ANIMATION
5034 static void ResetGfxFrame(int x, int y, boolean redraw)
5035 {
5036   int element = Feld[x][y];
5037   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5038   int last_gfx_frame = GfxFrame[x][y];
5039
5040   if (graphic_info[graphic].anim_global_sync)
5041     GfxFrame[x][y] = FrameCounter;
5042   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5043     GfxFrame[x][y] = CustomValue[x][y];
5044   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5045     GfxFrame[x][y] = element_info[element].collect_score;
5046   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5047     GfxFrame[x][y] = ChangeDelay[x][y];
5048
5049   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5050     DrawLevelGraphicAnimation(x, y, graphic);
5051 }
5052 #endif
5053
5054 static void ResetGfxAnimation(int x, int y)
5055 {
5056   GfxAction[x][y] = ACTION_DEFAULT;
5057   GfxDir[x][y] = MovDir[x][y];
5058   GfxFrame[x][y] = 0;
5059
5060 #if USE_GFX_RESET_GFX_ANIMATION
5061   ResetGfxFrame(x, y, FALSE);
5062 #endif
5063 }
5064
5065 static void ResetRandomAnimationValue(int x, int y)
5066 {
5067   GfxRandom[x][y] = INIT_GFX_RANDOM();
5068 }
5069
5070 void InitMovingField(int x, int y, int direction)
5071 {
5072   int element = Feld[x][y];
5073   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5074   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5075   int newx = x + dx;
5076   int newy = y + dy;
5077   boolean is_moving_before, is_moving_after;
5078 #if 0
5079   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5080 #endif
5081
5082   /* check if element was/is moving or being moved before/after mode change */
5083 #if 1
5084 #if 1
5085   is_moving_before = (WasJustMoving[x][y] != 0);
5086 #else
5087   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5088   is_moving_before = WasJustMoving[x][y];
5089 #endif
5090 #else
5091   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5092 #endif
5093   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5094
5095   /* reset animation only for moving elements which change direction of moving
5096      or which just started or stopped moving
5097      (else CEs with property "can move" / "not moving" are reset each frame) */
5098 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5099 #if 1
5100   if (is_moving_before != is_moving_after ||
5101       direction != MovDir[x][y])
5102     ResetGfxAnimation(x, y);
5103 #else
5104   if ((is_moving_before || is_moving_after) && !continues_moving)
5105     ResetGfxAnimation(x, y);
5106 #endif
5107 #else
5108   if (!continues_moving)
5109     ResetGfxAnimation(x, y);
5110 #endif
5111
5112   MovDir[x][y] = direction;
5113   GfxDir[x][y] = direction;
5114
5115 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5116   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5117                      direction == MV_DOWN && CAN_FALL(element) ?
5118                      ACTION_FALLING : ACTION_MOVING);
5119 #else
5120   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5121                      ACTION_FALLING : ACTION_MOVING);
5122 #endif
5123
5124   /* this is needed for CEs with property "can move" / "not moving" */
5125
5126   if (is_moving_after)
5127   {
5128     if (Feld[newx][newy] == EL_EMPTY)
5129       Feld[newx][newy] = EL_BLOCKED;
5130
5131     MovDir[newx][newy] = MovDir[x][y];
5132
5133 #if USE_NEW_CUSTOM_VALUE
5134     CustomValue[newx][newy] = CustomValue[x][y];
5135 #endif
5136
5137     GfxFrame[newx][newy] = GfxFrame[x][y];
5138     GfxRandom[newx][newy] = GfxRandom[x][y];
5139     GfxAction[newx][newy] = GfxAction[x][y];
5140     GfxDir[newx][newy] = GfxDir[x][y];
5141   }
5142 }
5143
5144 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5145 {
5146   int direction = MovDir[x][y];
5147   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5148   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5149
5150   *goes_to_x = newx;
5151   *goes_to_y = newy;
5152 }
5153
5154 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5155 {
5156   int oldx = x, oldy = y;
5157   int direction = MovDir[x][y];
5158
5159   if (direction == MV_LEFT)
5160     oldx++;
5161   else if (direction == MV_RIGHT)
5162     oldx--;
5163   else if (direction == MV_UP)
5164     oldy++;
5165   else if (direction == MV_DOWN)
5166     oldy--;
5167
5168   *comes_from_x = oldx;
5169   *comes_from_y = oldy;
5170 }
5171
5172 int MovingOrBlocked2Element(int x, int y)
5173 {
5174   int element = Feld[x][y];
5175
5176   if (element == EL_BLOCKED)
5177   {
5178     int oldx, oldy;
5179
5180     Blocked2Moving(x, y, &oldx, &oldy);
5181     return Feld[oldx][oldy];
5182   }
5183   else
5184     return element;
5185 }
5186
5187 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5188 {
5189   /* like MovingOrBlocked2Element(), but if element is moving
5190      and (x,y) is the field the moving element is just leaving,
5191      return EL_BLOCKED instead of the element value */
5192   int element = Feld[x][y];
5193
5194   if (IS_MOVING(x, y))
5195   {
5196     if (element == EL_BLOCKED)
5197     {
5198       int oldx, oldy;
5199
5200       Blocked2Moving(x, y, &oldx, &oldy);
5201       return Feld[oldx][oldy];
5202     }
5203     else
5204       return EL_BLOCKED;
5205   }
5206   else
5207     return element;
5208 }
5209
5210 static void RemoveField(int x, int y)
5211 {
5212   Feld[x][y] = EL_EMPTY;
5213
5214   MovPos[x][y] = 0;
5215   MovDir[x][y] = 0;
5216   MovDelay[x][y] = 0;
5217
5218 #if USE_NEW_CUSTOM_VALUE
5219   CustomValue[x][y] = 0;
5220 #endif
5221
5222   AmoebaNr[x][y] = 0;
5223   ChangeDelay[x][y] = 0;
5224   ChangePage[x][y] = -1;
5225   Pushed[x][y] = FALSE;
5226
5227 #if 0
5228   ExplodeField[x][y] = EX_TYPE_NONE;
5229 #endif
5230
5231   GfxElement[x][y] = EL_UNDEFINED;
5232   GfxAction[x][y] = ACTION_DEFAULT;
5233   GfxDir[x][y] = MV_NONE;
5234 #if 0
5235   /* !!! this would prevent the removed tile from being redrawn !!! */
5236   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5237 #endif
5238 }
5239
5240 void RemoveMovingField(int x, int y)
5241 {
5242   int oldx = x, oldy = y, newx = x, newy = y;
5243   int element = Feld[x][y];
5244   int next_element = EL_UNDEFINED;
5245
5246   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5247     return;
5248
5249   if (IS_MOVING(x, y))
5250   {
5251     Moving2Blocked(x, y, &newx, &newy);
5252
5253     if (Feld[newx][newy] != EL_BLOCKED)
5254     {
5255       /* element is moving, but target field is not free (blocked), but
5256          already occupied by something different (example: acid pool);
5257          in this case, only remove the moving field, but not the target */
5258
5259       RemoveField(oldx, oldy);
5260
5261       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5262
5263       TEST_DrawLevelField(oldx, oldy);
5264
5265       return;
5266     }
5267   }
5268   else if (element == EL_BLOCKED)
5269   {
5270     Blocked2Moving(x, y, &oldx, &oldy);
5271     if (!IS_MOVING(oldx, oldy))
5272       return;
5273   }
5274
5275   if (element == EL_BLOCKED &&
5276       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5277        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5278        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5279        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5280        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5281        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5282     next_element = get_next_element(Feld[oldx][oldy]);
5283
5284   RemoveField(oldx, oldy);
5285   RemoveField(newx, newy);
5286
5287   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5288
5289   if (next_element != EL_UNDEFINED)
5290     Feld[oldx][oldy] = next_element;
5291
5292   TEST_DrawLevelField(oldx, oldy);
5293   TEST_DrawLevelField(newx, newy);
5294 }
5295
5296 void DrawDynamite(int x, int y)
5297 {
5298   int sx = SCREENX(x), sy = SCREENY(y);
5299   int graphic = el2img(Feld[x][y]);
5300   int frame;
5301
5302   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5303     return;
5304
5305   if (IS_WALKABLE_INSIDE(Back[x][y]))
5306     return;
5307
5308   if (Back[x][y])
5309     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5310   else if (Store[x][y])
5311     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5312
5313   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5314
5315   if (Back[x][y] || Store[x][y])
5316     DrawGraphicThruMask(sx, sy, graphic, frame);
5317   else
5318     DrawGraphic(sx, sy, graphic, frame);
5319 }
5320
5321 void CheckDynamite(int x, int y)
5322 {
5323   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5324   {
5325     MovDelay[x][y]--;
5326
5327     if (MovDelay[x][y] != 0)
5328     {
5329       DrawDynamite(x, y);
5330       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5331
5332       return;
5333     }
5334   }
5335
5336   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5337
5338   Bang(x, y);
5339 }
5340
5341 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5342 {
5343   boolean num_checked_players = 0;
5344   int i;
5345
5346   for (i = 0; i < MAX_PLAYERS; i++)
5347   {
5348     if (stored_player[i].active)
5349     {
5350       int sx = stored_player[i].jx;
5351       int sy = stored_player[i].jy;
5352
5353       if (num_checked_players == 0)
5354       {
5355         *sx1 = *sx2 = sx;
5356         *sy1 = *sy2 = sy;
5357       }
5358       else
5359       {
5360         *sx1 = MIN(*sx1, sx);
5361         *sy1 = MIN(*sy1, sy);
5362         *sx2 = MAX(*sx2, sx);
5363         *sy2 = MAX(*sy2, sy);
5364       }
5365
5366       num_checked_players++;
5367     }
5368   }
5369 }
5370
5371 static boolean checkIfAllPlayersFitToScreen_RND()
5372 {
5373   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5374
5375   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5376
5377   return (sx2 - sx1 < SCR_FIELDX &&
5378           sy2 - sy1 < SCR_FIELDY);
5379 }
5380
5381 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5382 {
5383   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5384
5385   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5386
5387   *sx = (sx1 + sx2) / 2;
5388   *sy = (sy1 + sy2) / 2;
5389 }
5390
5391 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5392                         boolean center_screen, boolean quick_relocation)
5393 {
5394   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5395   boolean no_delay = (tape.warp_forward);
5396   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5397   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5398
5399   if (quick_relocation)
5400   {
5401     int offset = game.scroll_delay_value;
5402
5403     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5404     {
5405       if (!level.shifted_relocation || center_screen)
5406       {
5407         /* quick relocation (without scrolling), with centering of screen */
5408
5409         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5410                     x > SBX_Right + MIDPOSX ? SBX_Right :
5411                     x - MIDPOSX);
5412
5413         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5414                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5415                     y - MIDPOSY);
5416       }
5417       else
5418       {
5419         /* quick relocation (without scrolling), but do not center screen */
5420
5421         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5422                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5423                                old_x - MIDPOSX);
5424
5425         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5426                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5427                                old_y - MIDPOSY);
5428
5429         int offset_x = x + (scroll_x - center_scroll_x);
5430         int offset_y = y + (scroll_y - center_scroll_y);
5431
5432         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5433                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5434                     offset_x - MIDPOSX);
5435
5436         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5437                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5438                     offset_y - MIDPOSY);
5439       }
5440     }
5441     else
5442     {
5443       /* quick relocation (without scrolling), inside visible screen area */
5444
5445       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5446           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5447         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5448
5449       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5450           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5451         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5452
5453       /* don't scroll over playfield boundaries */
5454       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5455         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5456
5457       /* don't scroll over playfield boundaries */
5458       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5459         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5460     }
5461
5462     RedrawPlayfield(TRUE, 0,0,0,0);
5463   }
5464   else
5465   {
5466 #if 1
5467     int scroll_xx, scroll_yy;
5468
5469     if (!level.shifted_relocation || center_screen)
5470     {
5471       /* visible relocation (with scrolling), with centering of screen */
5472
5473       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5474                    x > SBX_Right + MIDPOSX ? SBX_Right :
5475                    x - MIDPOSX);
5476
5477       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5478                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5479                    y - MIDPOSY);
5480     }
5481     else
5482     {
5483       /* visible relocation (with scrolling), but do not center screen */
5484
5485       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5486                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5487                              old_x - MIDPOSX);
5488
5489       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5490                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5491                              old_y - MIDPOSY);
5492
5493       int offset_x = x + (scroll_x - center_scroll_x);
5494       int offset_y = y + (scroll_y - center_scroll_y);
5495
5496       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5497                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5498                    offset_x - MIDPOSX);
5499
5500       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5501                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5502                    offset_y - MIDPOSY);
5503     }
5504
5505 #else
5506
5507     /* visible relocation (with scrolling), with centering of screen */
5508
5509     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5510                      x > SBX_Right + MIDPOSX ? SBX_Right :
5511                      x - MIDPOSX);
5512
5513     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5514                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5515                      y - MIDPOSY);
5516 #endif
5517
5518     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5519
5520     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5521     {
5522       int dx = 0, dy = 0;
5523       int fx = FX, fy = FY;
5524
5525       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5526       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5527
5528       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5529         break;
5530
5531       scroll_x -= dx;
5532       scroll_y -= dy;
5533
5534       fx += dx * TILEX / 2;
5535       fy += dy * TILEY / 2;
5536
5537       ScrollLevel(dx, dy);
5538       DrawAllPlayers();
5539
5540       /* scroll in two steps of half tile size to make things smoother */
5541       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5542       FlushDisplay();
5543       Delay(wait_delay_value);
5544
5545       /* scroll second step to align at full tile size */
5546       BackToFront();
5547       Delay(wait_delay_value);
5548     }
5549
5550     DrawAllPlayers();
5551     BackToFront();
5552     Delay(wait_delay_value);
5553   }
5554 }
5555
5556 void RelocatePlayer(int jx, int jy, int el_player_raw)
5557 {
5558   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5559   int player_nr = GET_PLAYER_NR(el_player);
5560   struct PlayerInfo *player = &stored_player[player_nr];
5561   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5562   boolean no_delay = (tape.warp_forward);
5563   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5564   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5565   int old_jx = player->jx;
5566   int old_jy = player->jy;
5567   int old_element = Feld[old_jx][old_jy];
5568   int element = Feld[jx][jy];
5569   boolean player_relocated = (old_jx != jx || old_jy != jy);
5570
5571   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5572   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5573   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5574   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5575   int leave_side_horiz = move_dir_horiz;
5576   int leave_side_vert  = move_dir_vert;
5577   int enter_side = enter_side_horiz | enter_side_vert;
5578   int leave_side = leave_side_horiz | leave_side_vert;
5579
5580   if (player->GameOver)         /* do not reanimate dead player */
5581     return;
5582
5583   if (!player_relocated)        /* no need to relocate the player */
5584     return;
5585
5586   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5587   {
5588     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5589     DrawLevelField(jx, jy);
5590   }
5591
5592   if (player->present)
5593   {
5594     while (player->MovPos)
5595     {
5596       ScrollPlayer(player, SCROLL_GO_ON);
5597       ScrollScreen(NULL, SCROLL_GO_ON);
5598
5599       AdvanceFrameAndPlayerCounters(player->index_nr);
5600
5601       DrawPlayer(player);
5602
5603       BackToFront();
5604       Delay(wait_delay_value);
5605     }
5606
5607     DrawPlayer(player);         /* needed here only to cleanup last field */
5608     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5609
5610     player->is_moving = FALSE;
5611   }
5612
5613   if (IS_CUSTOM_ELEMENT(old_element))
5614     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5615                                CE_LEFT_BY_PLAYER,
5616                                player->index_bit, leave_side);
5617
5618   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5619                                       CE_PLAYER_LEAVES_X,
5620                                       player->index_bit, leave_side);
5621
5622   Feld[jx][jy] = el_player;
5623   InitPlayerField(jx, jy, el_player, TRUE);
5624
5625   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5626   {
5627     Feld[jx][jy] = element;
5628     InitField(jx, jy, FALSE);
5629   }
5630
5631   /* only visually relocate centered player */
5632   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5633                      FALSE, level.instant_relocation);
5634
5635   TestIfPlayerTouchesBadThing(jx, jy);
5636   TestIfPlayerTouchesCustomElement(jx, jy);
5637
5638   if (IS_CUSTOM_ELEMENT(element))
5639     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5640                                player->index_bit, enter_side);
5641
5642   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5643                                       player->index_bit, enter_side);
5644 }
5645
5646 void Explode(int ex, int ey, int phase, int mode)
5647 {
5648   int x, y;
5649   int last_phase;
5650   int border_element;
5651
5652   /* !!! eliminate this variable !!! */
5653   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5654
5655   if (game.explosions_delayed)
5656   {
5657     ExplodeField[ex][ey] = mode;
5658     return;
5659   }
5660
5661   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5662   {
5663     int center_element = Feld[ex][ey];
5664     int artwork_element, explosion_element;     /* set these values later */
5665
5666 #if 0
5667     /* --- This is only really needed (and now handled) in "Impact()". --- */
5668     /* do not explode moving elements that left the explode field in time */
5669     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5670         center_element == EL_EMPTY &&
5671         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5672       return;
5673 #endif
5674
5675 #if 0
5676     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5677     if (mode == EX_TYPE_NORMAL ||
5678         mode == EX_TYPE_CENTER ||
5679         mode == EX_TYPE_CROSS)
5680       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5681 #endif
5682
5683     /* remove things displayed in background while burning dynamite */
5684     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5685       Back[ex][ey] = 0;
5686
5687     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5688     {
5689       /* put moving element to center field (and let it explode there) */
5690       center_element = MovingOrBlocked2Element(ex, ey);
5691       RemoveMovingField(ex, ey);
5692       Feld[ex][ey] = center_element;
5693     }
5694
5695     /* now "center_element" is finally determined -- set related values now */
5696     artwork_element = center_element;           /* for custom player artwork */
5697     explosion_element = center_element;         /* for custom player artwork */
5698
5699     if (IS_PLAYER(ex, ey))
5700     {
5701       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5702
5703       artwork_element = stored_player[player_nr].artwork_element;
5704
5705       if (level.use_explosion_element[player_nr])
5706       {
5707         explosion_element = level.explosion_element[player_nr];
5708         artwork_element = explosion_element;
5709       }
5710     }
5711
5712 #if 1
5713     if (mode == EX_TYPE_NORMAL ||
5714         mode == EX_TYPE_CENTER ||
5715         mode == EX_TYPE_CROSS)
5716       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5717 #endif
5718
5719     last_phase = element_info[explosion_element].explosion_delay + 1;
5720
5721     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5722     {
5723       int xx = x - ex + 1;
5724       int yy = y - ey + 1;
5725       int element;
5726
5727       if (!IN_LEV_FIELD(x, y) ||
5728           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5729           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5730         continue;
5731
5732       element = Feld[x][y];
5733
5734       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5735       {
5736         element = MovingOrBlocked2Element(x, y);
5737
5738         if (!IS_EXPLOSION_PROOF(element))
5739           RemoveMovingField(x, y);
5740       }
5741
5742       /* indestructible elements can only explode in center (but not flames) */
5743       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5744                                            mode == EX_TYPE_BORDER)) ||
5745           element == EL_FLAMES)
5746         continue;
5747
5748       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5749          behaviour, for example when touching a yamyam that explodes to rocks
5750          with active deadly shield, a rock is created under the player !!! */
5751       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5752 #if 0
5753       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5754           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5755            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5756 #else
5757       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5758 #endif
5759       {
5760         if (IS_ACTIVE_BOMB(element))
5761         {
5762           /* re-activate things under the bomb like gate or penguin */
5763           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5764           Back[x][y] = 0;
5765         }
5766
5767         continue;
5768       }
5769
5770       /* save walkable background elements while explosion on same tile */
5771       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5772           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5773         Back[x][y] = element;
5774
5775       /* ignite explodable elements reached by other explosion */
5776       if (element == EL_EXPLOSION)
5777         element = Store2[x][y];
5778
5779       if (AmoebaNr[x][y] &&
5780           (element == EL_AMOEBA_FULL ||
5781            element == EL_BD_AMOEBA ||
5782            element == EL_AMOEBA_GROWING))
5783       {
5784         AmoebaCnt[AmoebaNr[x][y]]--;
5785         AmoebaCnt2[AmoebaNr[x][y]]--;
5786       }
5787
5788       RemoveField(x, y);
5789
5790       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5791       {
5792         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5793
5794         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5795
5796         if (PLAYERINFO(ex, ey)->use_murphy)
5797           Store[x][y] = EL_EMPTY;
5798       }
5799
5800       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5801          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5802       else if (ELEM_IS_PLAYER(center_element))
5803         Store[x][y] = EL_EMPTY;
5804       else if (center_element == EL_YAMYAM)
5805         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5806       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5807         Store[x][y] = element_info[center_element].content.e[xx][yy];
5808 #if 1
5809       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5810          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5811          otherwise) -- FIX THIS !!! */
5812       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5813         Store[x][y] = element_info[element].content.e[1][1];
5814 #else
5815       else if (!CAN_EXPLODE(element))
5816         Store[x][y] = element_info[element].content.e[1][1];
5817 #endif
5818       else
5819         Store[x][y] = EL_EMPTY;
5820
5821       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5822           center_element == EL_AMOEBA_TO_DIAMOND)
5823         Store2[x][y] = element;
5824
5825       Feld[x][y] = EL_EXPLOSION;
5826       GfxElement[x][y] = artwork_element;
5827
5828       ExplodePhase[x][y] = 1;
5829       ExplodeDelay[x][y] = last_phase;
5830
5831       Stop[x][y] = TRUE;
5832     }
5833
5834     if (center_element == EL_YAMYAM)
5835       game.yamyam_content_nr =
5836         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5837
5838     return;
5839   }
5840
5841   if (Stop[ex][ey])
5842     return;
5843
5844   x = ex;
5845   y = ey;
5846
5847   if (phase == 1)
5848     GfxFrame[x][y] = 0;         /* restart explosion animation */
5849
5850   last_phase = ExplodeDelay[x][y];
5851
5852   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5853
5854 #ifdef DEBUG
5855
5856   /* activate this even in non-DEBUG version until cause for crash in
5857      getGraphicAnimationFrame() (see below) is found and eliminated */
5858
5859 #endif
5860 #if 1
5861
5862 #if 1
5863   /* this can happen if the player leaves an explosion just in time */
5864   if (GfxElement[x][y] == EL_UNDEFINED)
5865     GfxElement[x][y] = EL_EMPTY;
5866 #else
5867   if (GfxElement[x][y] == EL_UNDEFINED)
5868   {
5869     printf("\n\n");
5870     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5871     printf("Explode(): This should never happen!\n");
5872     printf("\n\n");
5873
5874     GfxElement[x][y] = EL_EMPTY;
5875   }
5876 #endif
5877
5878 #endif
5879
5880   border_element = Store2[x][y];
5881   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5882     border_element = StorePlayer[x][y];
5883
5884   if (phase == element_info[border_element].ignition_delay ||
5885       phase == last_phase)
5886   {
5887     boolean border_explosion = FALSE;
5888
5889     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5890         !PLAYER_EXPLOSION_PROTECTED(x, y))
5891     {
5892       KillPlayerUnlessExplosionProtected(x, y);
5893       border_explosion = TRUE;
5894     }
5895     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5896     {
5897       Feld[x][y] = Store2[x][y];
5898       Store2[x][y] = 0;
5899       Bang(x, y);
5900       border_explosion = TRUE;
5901     }
5902     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5903     {
5904       AmoebeUmwandeln(x, y);
5905       Store2[x][y] = 0;
5906       border_explosion = TRUE;
5907     }
5908
5909     /* if an element just explodes due to another explosion (chain-reaction),
5910        do not immediately end the new explosion when it was the last frame of
5911        the explosion (as it would be done in the following "if"-statement!) */
5912     if (border_explosion && phase == last_phase)
5913       return;
5914   }
5915
5916   if (phase == last_phase)
5917   {
5918     int element;
5919
5920     element = Feld[x][y] = Store[x][y];
5921     Store[x][y] = Store2[x][y] = 0;
5922     GfxElement[x][y] = EL_UNDEFINED;
5923
5924     /* player can escape from explosions and might therefore be still alive */
5925     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5926         element <= EL_PLAYER_IS_EXPLODING_4)
5927     {
5928       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5929       int explosion_element = EL_PLAYER_1 + player_nr;
5930       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5931       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5932
5933       if (level.use_explosion_element[player_nr])
5934         explosion_element = level.explosion_element[player_nr];
5935
5936       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5937                     element_info[explosion_element].content.e[xx][yy]);
5938     }
5939
5940     /* restore probably existing indestructible background element */
5941     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5942       element = Feld[x][y] = Back[x][y];
5943     Back[x][y] = 0;
5944
5945     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5946     GfxDir[x][y] = MV_NONE;
5947     ChangeDelay[x][y] = 0;
5948     ChangePage[x][y] = -1;
5949
5950 #if USE_NEW_CUSTOM_VALUE
5951     CustomValue[x][y] = 0;
5952 #endif
5953
5954     InitField_WithBug2(x, y, FALSE);
5955
5956     TEST_DrawLevelField(x, y);
5957
5958     TestIfElementTouchesCustomElement(x, y);
5959
5960     if (GFX_CRUMBLED(element))
5961       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5962
5963     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5964       StorePlayer[x][y] = 0;
5965
5966     if (ELEM_IS_PLAYER(element))
5967       RelocatePlayer(x, y, element);
5968   }
5969   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5970   {
5971     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5972     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5973
5974     if (phase == delay)
5975       TEST_DrawLevelFieldCrumbledSand(x, y);
5976
5977     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5978     {
5979       DrawLevelElement(x, y, Back[x][y]);
5980       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5981     }
5982     else if (IS_WALKABLE_UNDER(Back[x][y]))
5983     {
5984       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5985       DrawLevelElementThruMask(x, y, Back[x][y]);
5986     }
5987     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5988       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5989   }
5990 }
5991
5992 void DynaExplode(int ex, int ey)
5993 {
5994   int i, j;
5995   int dynabomb_element = Feld[ex][ey];
5996   int dynabomb_size = 1;
5997   boolean dynabomb_xl = FALSE;
5998   struct PlayerInfo *player;
5999   static int xy[4][2] =
6000   {
6001     { 0, -1 },
6002     { -1, 0 },
6003     { +1, 0 },
6004     { 0, +1 }
6005   };
6006
6007   if (IS_ACTIVE_BOMB(dynabomb_element))
6008   {
6009     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6010     dynabomb_size = player->dynabomb_size;
6011     dynabomb_xl = player->dynabomb_xl;
6012     player->dynabombs_left++;
6013   }
6014
6015   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6016
6017   for (i = 0; i < NUM_DIRECTIONS; i++)
6018   {
6019     for (j = 1; j <= dynabomb_size; j++)
6020     {
6021       int x = ex + j * xy[i][0];
6022       int y = ey + j * xy[i][1];
6023       int element;
6024
6025       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6026         break;
6027
6028       element = Feld[x][y];
6029
6030       /* do not restart explosions of fields with active bombs */
6031       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6032         continue;
6033
6034       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6035
6036       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6037           !IS_DIGGABLE(element) && !dynabomb_xl)
6038         break;
6039     }
6040   }
6041 }
6042
6043 void Bang(int x, int y)
6044 {
6045   int element = MovingOrBlocked2Element(x, y);
6046   int explosion_type = EX_TYPE_NORMAL;
6047
6048   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6049   {
6050     struct PlayerInfo *player = PLAYERINFO(x, y);
6051
6052 #if USE_FIX_CE_ACTION_WITH_PLAYER
6053     element = Feld[x][y] = player->initial_element;
6054 #else
6055     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6056                             player->element_nr);
6057 #endif
6058
6059     if (level.use_explosion_element[player->index_nr])
6060     {
6061       int explosion_element = level.explosion_element[player->index_nr];
6062
6063       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6064         explosion_type = EX_TYPE_CROSS;
6065       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6066         explosion_type = EX_TYPE_CENTER;
6067     }
6068   }
6069
6070   switch (element)
6071   {
6072     case EL_BUG:
6073     case EL_SPACESHIP:
6074     case EL_BD_BUTTERFLY:
6075     case EL_BD_FIREFLY:
6076     case EL_YAMYAM:
6077     case EL_DARK_YAMYAM:
6078     case EL_ROBOT:
6079     case EL_PACMAN:
6080     case EL_MOLE:
6081       RaiseScoreElement(element);
6082       break;
6083
6084     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6085     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6086     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6087     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6088     case EL_DYNABOMB_INCREASE_NUMBER:
6089     case EL_DYNABOMB_INCREASE_SIZE:
6090     case EL_DYNABOMB_INCREASE_POWER:
6091       explosion_type = EX_TYPE_DYNA;
6092       break;
6093
6094     case EL_DC_LANDMINE:
6095 #if 0
6096     case EL_EM_EXIT_OPEN:
6097     case EL_EM_STEEL_EXIT_OPEN:
6098 #endif
6099       explosion_type = EX_TYPE_CENTER;
6100       break;
6101
6102     case EL_PENGUIN:
6103     case EL_LAMP:
6104     case EL_LAMP_ACTIVE:
6105     case EL_AMOEBA_TO_DIAMOND:
6106       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6107         explosion_type = EX_TYPE_CENTER;
6108       break;
6109
6110     default:
6111       if (element_info[element].explosion_type == EXPLODES_CROSS)
6112         explosion_type = EX_TYPE_CROSS;
6113       else if (element_info[element].explosion_type == EXPLODES_1X1)
6114         explosion_type = EX_TYPE_CENTER;
6115       break;
6116   }
6117
6118   if (explosion_type == EX_TYPE_DYNA)
6119     DynaExplode(x, y);
6120   else
6121     Explode(x, y, EX_PHASE_START, explosion_type);
6122
6123   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6124 }
6125
6126 void SplashAcid(int x, int y)
6127 {
6128   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6129       (!IN_LEV_FIELD(x - 1, y - 2) ||
6130        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6131     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6132
6133   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6134       (!IN_LEV_FIELD(x + 1, y - 2) ||
6135        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6136     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6137
6138   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6139 }
6140
6141 static void InitBeltMovement()
6142 {
6143   static int belt_base_element[4] =
6144   {
6145     EL_CONVEYOR_BELT_1_LEFT,
6146     EL_CONVEYOR_BELT_2_LEFT,
6147     EL_CONVEYOR_BELT_3_LEFT,
6148     EL_CONVEYOR_BELT_4_LEFT
6149   };
6150   static int belt_base_active_element[4] =
6151   {
6152     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6153     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6154     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6155     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6156   };
6157
6158   int x, y, i, j;
6159
6160   /* set frame order for belt animation graphic according to belt direction */
6161   for (i = 0; i < NUM_BELTS; i++)
6162   {
6163     int belt_nr = i;
6164
6165     for (j = 0; j < NUM_BELT_PARTS; j++)
6166     {
6167       int element = belt_base_active_element[belt_nr] + j;
6168       int graphic_1 = el2img(element);
6169       int graphic_2 = el2panelimg(element);
6170
6171       if (game.belt_dir[i] == MV_LEFT)
6172       {
6173         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6174         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6175       }
6176       else
6177       {
6178         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6179         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6180       }
6181     }
6182   }
6183
6184   SCAN_PLAYFIELD(x, y)
6185   {
6186     int element = Feld[x][y];
6187
6188     for (i = 0; i < NUM_BELTS; i++)
6189     {
6190       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6191       {
6192         int e_belt_nr = getBeltNrFromBeltElement(element);
6193         int belt_nr = i;
6194
6195         if (e_belt_nr == belt_nr)
6196         {
6197           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6198
6199           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6200         }
6201       }
6202     }
6203   }
6204 }
6205
6206 static void ToggleBeltSwitch(int x, int y)
6207 {
6208   static int belt_base_element[4] =
6209   {
6210     EL_CONVEYOR_BELT_1_LEFT,
6211     EL_CONVEYOR_BELT_2_LEFT,
6212     EL_CONVEYOR_BELT_3_LEFT,
6213     EL_CONVEYOR_BELT_4_LEFT
6214   };
6215   static int belt_base_active_element[4] =
6216   {
6217     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6218     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6219     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6220     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6221   };
6222   static int belt_base_switch_element[4] =
6223   {
6224     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6225     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6226     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6227     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6228   };
6229   static int belt_move_dir[4] =
6230   {
6231     MV_LEFT,
6232     MV_NONE,
6233     MV_RIGHT,
6234     MV_NONE,
6235   };
6236
6237   int element = Feld[x][y];
6238   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6239   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6240   int belt_dir = belt_move_dir[belt_dir_nr];
6241   int xx, yy, i;
6242
6243   if (!IS_BELT_SWITCH(element))
6244     return;
6245
6246   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6247   game.belt_dir[belt_nr] = belt_dir;
6248
6249   if (belt_dir_nr == 3)
6250     belt_dir_nr = 1;
6251
6252   /* set frame order for belt animation graphic according to belt direction */
6253   for (i = 0; i < NUM_BELT_PARTS; i++)
6254   {
6255     int element = belt_base_active_element[belt_nr] + i;
6256     int graphic_1 = el2img(element);
6257     int graphic_2 = el2panelimg(element);
6258
6259     if (belt_dir == MV_LEFT)
6260     {
6261       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6262       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6263     }
6264     else
6265     {
6266       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6267       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6268     }
6269   }
6270
6271   SCAN_PLAYFIELD(xx, yy)
6272   {
6273     int element = Feld[xx][yy];
6274
6275     if (IS_BELT_SWITCH(element))
6276     {
6277       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6278
6279       if (e_belt_nr == belt_nr)
6280       {
6281         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6282         TEST_DrawLevelField(xx, yy);
6283       }
6284     }
6285     else if (IS_BELT(element) && belt_dir != MV_NONE)
6286     {
6287       int e_belt_nr = getBeltNrFromBeltElement(element);
6288
6289       if (e_belt_nr == belt_nr)
6290       {
6291         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6292
6293         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6294         TEST_DrawLevelField(xx, yy);
6295       }
6296     }
6297     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6298     {
6299       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6300
6301       if (e_belt_nr == belt_nr)
6302       {
6303         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6304
6305         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6306         TEST_DrawLevelField(xx, yy);
6307       }
6308     }
6309   }
6310 }
6311
6312 static void ToggleSwitchgateSwitch(int x, int y)
6313 {
6314   int xx, yy;
6315
6316   game.switchgate_pos = !game.switchgate_pos;
6317
6318   SCAN_PLAYFIELD(xx, yy)
6319   {
6320     int element = Feld[xx][yy];
6321
6322 #if !USE_BOTH_SWITCHGATE_SWITCHES
6323     if (element == EL_SWITCHGATE_SWITCH_UP ||
6324         element == EL_SWITCHGATE_SWITCH_DOWN)
6325     {
6326       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6327       TEST_DrawLevelField(xx, yy);
6328     }
6329     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6330              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6331     {
6332       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6333       TEST_DrawLevelField(xx, yy);
6334     }
6335 #else
6336     if (element == EL_SWITCHGATE_SWITCH_UP)
6337     {
6338       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6339       TEST_DrawLevelField(xx, yy);
6340     }
6341     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6342     {
6343       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6344       TEST_DrawLevelField(xx, yy);
6345     }
6346     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6347     {
6348       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6349       TEST_DrawLevelField(xx, yy);
6350     }
6351     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6352     {
6353       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6354       TEST_DrawLevelField(xx, yy);
6355     }
6356 #endif
6357     else if (element == EL_SWITCHGATE_OPEN ||
6358              element == EL_SWITCHGATE_OPENING)
6359     {
6360       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6361
6362       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6363     }
6364     else if (element == EL_SWITCHGATE_CLOSED ||
6365              element == EL_SWITCHGATE_CLOSING)
6366     {
6367       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6368
6369       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6370     }
6371   }
6372 }
6373
6374 static int getInvisibleActiveFromInvisibleElement(int element)
6375 {
6376   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6377           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6378           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6379           element);
6380 }
6381
6382 static int getInvisibleFromInvisibleActiveElement(int element)
6383 {
6384   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6385           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6386           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6387           element);
6388 }
6389
6390 static void RedrawAllLightSwitchesAndInvisibleElements()
6391 {
6392   int x, y;
6393
6394   SCAN_PLAYFIELD(x, y)
6395   {
6396     int element = Feld[x][y];
6397
6398     if (element == EL_LIGHT_SWITCH &&
6399         game.light_time_left > 0)
6400     {
6401       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6402       TEST_DrawLevelField(x, y);
6403     }
6404     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6405              game.light_time_left == 0)
6406     {
6407       Feld[x][y] = EL_LIGHT_SWITCH;
6408       TEST_DrawLevelField(x, y);
6409     }
6410     else if (element == EL_EMC_DRIPPER &&
6411              game.light_time_left > 0)
6412     {
6413       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6414       TEST_DrawLevelField(x, y);
6415     }
6416     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6417              game.light_time_left == 0)
6418     {
6419       Feld[x][y] = EL_EMC_DRIPPER;
6420       TEST_DrawLevelField(x, y);
6421     }
6422     else if (element == EL_INVISIBLE_STEELWALL ||
6423              element == EL_INVISIBLE_WALL ||
6424              element == EL_INVISIBLE_SAND)
6425     {
6426       if (game.light_time_left > 0)
6427         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6428
6429       TEST_DrawLevelField(x, y);
6430
6431       /* uncrumble neighbour fields, if needed */
6432       if (element == EL_INVISIBLE_SAND)
6433         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6434     }
6435     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6436              element == EL_INVISIBLE_WALL_ACTIVE ||
6437              element == EL_INVISIBLE_SAND_ACTIVE)
6438     {
6439       if (game.light_time_left == 0)
6440         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6441
6442       TEST_DrawLevelField(x, y);
6443
6444       /* re-crumble neighbour fields, if needed */
6445       if (element == EL_INVISIBLE_SAND)
6446         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6447     }
6448   }
6449 }
6450
6451 static void RedrawAllInvisibleElementsForLenses()
6452 {
6453   int x, y;
6454
6455   SCAN_PLAYFIELD(x, y)
6456   {
6457     int element = Feld[x][y];
6458
6459     if (element == EL_EMC_DRIPPER &&
6460         game.lenses_time_left > 0)
6461     {
6462       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6463       TEST_DrawLevelField(x, y);
6464     }
6465     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6466              game.lenses_time_left == 0)
6467     {
6468       Feld[x][y] = EL_EMC_DRIPPER;
6469       TEST_DrawLevelField(x, y);
6470     }
6471     else if (element == EL_INVISIBLE_STEELWALL ||
6472              element == EL_INVISIBLE_WALL ||
6473              element == EL_INVISIBLE_SAND)
6474     {
6475       if (game.lenses_time_left > 0)
6476         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6477
6478       TEST_DrawLevelField(x, y);
6479
6480       /* uncrumble neighbour fields, if needed */
6481       if (element == EL_INVISIBLE_SAND)
6482         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6483     }
6484     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6485              element == EL_INVISIBLE_WALL_ACTIVE ||
6486              element == EL_INVISIBLE_SAND_ACTIVE)
6487     {
6488       if (game.lenses_time_left == 0)
6489         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6490
6491       TEST_DrawLevelField(x, y);
6492
6493       /* re-crumble neighbour fields, if needed */
6494       if (element == EL_INVISIBLE_SAND)
6495         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6496     }
6497   }
6498 }
6499
6500 static void RedrawAllInvisibleElementsForMagnifier()
6501 {
6502   int x, y;
6503
6504   SCAN_PLAYFIELD(x, y)
6505   {
6506     int element = Feld[x][y];
6507
6508     if (element == EL_EMC_FAKE_GRASS &&
6509         game.magnify_time_left > 0)
6510     {
6511       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6512       TEST_DrawLevelField(x, y);
6513     }
6514     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6515              game.magnify_time_left == 0)
6516     {
6517       Feld[x][y] = EL_EMC_FAKE_GRASS;
6518       TEST_DrawLevelField(x, y);
6519     }
6520     else if (IS_GATE_GRAY(element) &&
6521              game.magnify_time_left > 0)
6522     {
6523       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6524                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6525                     IS_EM_GATE_GRAY(element) ?
6526                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6527                     IS_EMC_GATE_GRAY(element) ?
6528                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6529                     IS_DC_GATE_GRAY(element) ?
6530                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6531                     element);
6532       TEST_DrawLevelField(x, y);
6533     }
6534     else if (IS_GATE_GRAY_ACTIVE(element) &&
6535              game.magnify_time_left == 0)
6536     {
6537       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6538                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6539                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6540                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6541                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6542                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6543                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6544                     EL_DC_GATE_WHITE_GRAY :
6545                     element);
6546       TEST_DrawLevelField(x, y);
6547     }
6548   }
6549 }
6550
6551 static void ToggleLightSwitch(int x, int y)
6552 {
6553   int element = Feld[x][y];
6554
6555   game.light_time_left =
6556     (element == EL_LIGHT_SWITCH ?
6557      level.time_light * FRAMES_PER_SECOND : 0);
6558
6559   RedrawAllLightSwitchesAndInvisibleElements();
6560 }
6561
6562 static void ActivateTimegateSwitch(int x, int y)
6563 {
6564   int xx, yy;
6565
6566   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6567
6568   SCAN_PLAYFIELD(xx, yy)
6569   {
6570     int element = Feld[xx][yy];
6571
6572     if (element == EL_TIMEGATE_CLOSED ||
6573         element == EL_TIMEGATE_CLOSING)
6574     {
6575       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6576       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6577     }
6578
6579     /*
6580     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6581     {
6582       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6583       TEST_DrawLevelField(xx, yy);
6584     }
6585     */
6586
6587   }
6588
6589 #if 1
6590   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6591                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6592 #else
6593   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6594 #endif
6595 }
6596
6597 void Impact(int x, int y)
6598 {
6599   boolean last_line = (y == lev_fieldy - 1);
6600   boolean object_hit = FALSE;
6601   boolean impact = (last_line || object_hit);
6602   int element = Feld[x][y];
6603   int smashed = EL_STEELWALL;
6604
6605   if (!last_line)       /* check if element below was hit */
6606   {
6607     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6608       return;
6609
6610     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6611                                          MovDir[x][y + 1] != MV_DOWN ||
6612                                          MovPos[x][y + 1] <= TILEY / 2));
6613
6614     /* do not smash moving elements that left the smashed field in time */
6615     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6616         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6617       object_hit = FALSE;
6618
6619 #if USE_QUICKSAND_IMPACT_BUGFIX
6620     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6621     {
6622       RemoveMovingField(x, y + 1);
6623       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6624       Feld[x][y + 2] = EL_ROCK;
6625       TEST_DrawLevelField(x, y + 2);
6626
6627       object_hit = TRUE;
6628     }
6629
6630     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6631     {
6632       RemoveMovingField(x, y + 1);
6633       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6634       Feld[x][y + 2] = EL_ROCK;
6635       TEST_DrawLevelField(x, y + 2);
6636
6637       object_hit = TRUE;
6638     }
6639 #endif
6640
6641     if (object_hit)
6642       smashed = MovingOrBlocked2Element(x, y + 1);
6643
6644     impact = (last_line || object_hit);
6645   }
6646
6647   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6648   {
6649     SplashAcid(x, y + 1);
6650     return;
6651   }
6652
6653   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6654   /* only reset graphic animation if graphic really changes after impact */
6655   if (impact &&
6656       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6657   {
6658     ResetGfxAnimation(x, y);
6659     TEST_DrawLevelField(x, y);
6660   }
6661
6662   if (impact && CAN_EXPLODE_IMPACT(element))
6663   {
6664     Bang(x, y);
6665     return;
6666   }
6667   else if (impact && element == EL_PEARL &&
6668            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6669   {
6670     ResetGfxAnimation(x, y);
6671
6672     Feld[x][y] = EL_PEARL_BREAKING;
6673     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6674     return;
6675   }
6676   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6677   {
6678     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6679
6680     return;
6681   }
6682
6683   if (impact && element == EL_AMOEBA_DROP)
6684   {
6685     if (object_hit && IS_PLAYER(x, y + 1))
6686       KillPlayerUnlessEnemyProtected(x, y + 1);
6687     else if (object_hit && smashed == EL_PENGUIN)
6688       Bang(x, y + 1);
6689     else
6690     {
6691       Feld[x][y] = EL_AMOEBA_GROWING;
6692       Store[x][y] = EL_AMOEBA_WET;
6693
6694       ResetRandomAnimationValue(x, y);
6695     }
6696     return;
6697   }
6698
6699   if (object_hit)               /* check which object was hit */
6700   {
6701     if ((CAN_PASS_MAGIC_WALL(element) && 
6702          (smashed == EL_MAGIC_WALL ||
6703           smashed == EL_BD_MAGIC_WALL)) ||
6704         (CAN_PASS_DC_MAGIC_WALL(element) &&
6705          smashed == EL_DC_MAGIC_WALL))
6706     {
6707       int xx, yy;
6708       int activated_magic_wall =
6709         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6710          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6711          EL_DC_MAGIC_WALL_ACTIVE);
6712
6713       /* activate magic wall / mill */
6714       SCAN_PLAYFIELD(xx, yy)
6715       {
6716         if (Feld[xx][yy] == smashed)
6717           Feld[xx][yy] = activated_magic_wall;
6718       }
6719
6720       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6721       game.magic_wall_active = TRUE;
6722
6723       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6724                             SND_MAGIC_WALL_ACTIVATING :
6725                             smashed == EL_BD_MAGIC_WALL ?
6726                             SND_BD_MAGIC_WALL_ACTIVATING :
6727                             SND_DC_MAGIC_WALL_ACTIVATING));
6728     }
6729
6730     if (IS_PLAYER(x, y + 1))
6731     {
6732       if (CAN_SMASH_PLAYER(element))
6733       {
6734         KillPlayerUnlessEnemyProtected(x, y + 1);
6735         return;
6736       }
6737     }
6738     else if (smashed == EL_PENGUIN)
6739     {
6740       if (CAN_SMASH_PLAYER(element))
6741       {
6742         Bang(x, y + 1);
6743         return;
6744       }
6745     }
6746     else if (element == EL_BD_DIAMOND)
6747     {
6748       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6749       {
6750         Bang(x, y + 1);
6751         return;
6752       }
6753     }
6754     else if (((element == EL_SP_INFOTRON ||
6755                element == EL_SP_ZONK) &&
6756               (smashed == EL_SP_SNIKSNAK ||
6757                smashed == EL_SP_ELECTRON ||
6758                smashed == EL_SP_DISK_ORANGE)) ||
6759              (element == EL_SP_INFOTRON &&
6760               smashed == EL_SP_DISK_YELLOW))
6761     {
6762       Bang(x, y + 1);
6763       return;
6764     }
6765     else if (CAN_SMASH_EVERYTHING(element))
6766     {
6767       if (IS_CLASSIC_ENEMY(smashed) ||
6768           CAN_EXPLODE_SMASHED(smashed))
6769       {
6770         Bang(x, y + 1);
6771         return;
6772       }
6773       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6774       {
6775         if (smashed == EL_LAMP ||
6776             smashed == EL_LAMP_ACTIVE)
6777         {
6778           Bang(x, y + 1);
6779           return;
6780         }
6781         else if (smashed == EL_NUT)
6782         {
6783           Feld[x][y + 1] = EL_NUT_BREAKING;
6784           PlayLevelSound(x, y, SND_NUT_BREAKING);
6785           RaiseScoreElement(EL_NUT);
6786           return;
6787         }
6788         else if (smashed == EL_PEARL)
6789         {
6790           ResetGfxAnimation(x, y);
6791
6792           Feld[x][y + 1] = EL_PEARL_BREAKING;
6793           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6794           return;
6795         }
6796         else if (smashed == EL_DIAMOND)
6797         {
6798           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6799           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6800           return;
6801         }
6802         else if (IS_BELT_SWITCH(smashed))
6803         {
6804           ToggleBeltSwitch(x, y + 1);
6805         }
6806         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6807                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6808                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6809                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6810         {
6811           ToggleSwitchgateSwitch(x, y + 1);
6812         }
6813         else if (smashed == EL_LIGHT_SWITCH ||
6814                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6815         {
6816           ToggleLightSwitch(x, y + 1);
6817         }
6818         else
6819         {
6820 #if 0
6821           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6822 #endif
6823
6824           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6825
6826           CheckElementChangeBySide(x, y + 1, smashed, element,
6827                                    CE_SWITCHED, CH_SIDE_TOP);
6828           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6829                                             CH_SIDE_TOP);
6830         }
6831       }
6832       else
6833       {
6834         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6835       }
6836     }
6837   }
6838
6839   /* play sound of magic wall / mill */
6840   if (!last_line &&
6841       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6842        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6843        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6844   {
6845     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6846       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6847     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6848       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6849     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6850       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6851
6852     return;
6853   }
6854
6855   /* play sound of object that hits the ground */
6856   if (last_line || object_hit)
6857     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6858 }
6859
6860 inline static void TurnRoundExt(int x, int y)
6861 {
6862   static struct
6863   {
6864     int dx, dy;
6865   } move_xy[] =
6866   {
6867     {  0,  0 },
6868     { -1,  0 },
6869     { +1,  0 },
6870     {  0,  0 },
6871     {  0, -1 },
6872     {  0,  0 }, { 0, 0 }, { 0, 0 },
6873     {  0, +1 }
6874   };
6875   static struct
6876   {
6877     int left, right, back;
6878   } turn[] =
6879   {
6880     { 0,        0,              0        },
6881     { MV_DOWN,  MV_UP,          MV_RIGHT },
6882     { MV_UP,    MV_DOWN,        MV_LEFT  },
6883     { 0,        0,              0        },
6884     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6885     { 0,        0,              0        },
6886     { 0,        0,              0        },
6887     { 0,        0,              0        },
6888     { MV_RIGHT, MV_LEFT,        MV_UP    }
6889   };
6890
6891   int element = Feld[x][y];
6892   int move_pattern = element_info[element].move_pattern;
6893
6894   int old_move_dir = MovDir[x][y];
6895   int left_dir  = turn[old_move_dir].left;
6896   int right_dir = turn[old_move_dir].right;
6897   int back_dir  = turn[old_move_dir].back;
6898
6899   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6900   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6901   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6902   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6903
6904   int left_x  = x + left_dx,  left_y  = y + left_dy;
6905   int right_x = x + right_dx, right_y = y + right_dy;
6906   int move_x  = x + move_dx,  move_y  = y + move_dy;
6907
6908   int xx, yy;
6909
6910   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6911   {
6912     TestIfBadThingTouchesOtherBadThing(x, y);
6913
6914     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6915       MovDir[x][y] = right_dir;
6916     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6917       MovDir[x][y] = left_dir;
6918
6919     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6920       MovDelay[x][y] = 9;
6921     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6922       MovDelay[x][y] = 1;
6923   }
6924   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6925   {
6926     TestIfBadThingTouchesOtherBadThing(x, y);
6927
6928     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6929       MovDir[x][y] = left_dir;
6930     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6931       MovDir[x][y] = right_dir;
6932
6933     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6934       MovDelay[x][y] = 9;
6935     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6936       MovDelay[x][y] = 1;
6937   }
6938   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6939   {
6940     TestIfBadThingTouchesOtherBadThing(x, y);
6941
6942     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6943       MovDir[x][y] = left_dir;
6944     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6945       MovDir[x][y] = right_dir;
6946
6947     if (MovDir[x][y] != old_move_dir)
6948       MovDelay[x][y] = 9;
6949   }
6950   else if (element == EL_YAMYAM)
6951   {
6952     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6953     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6954
6955     if (can_turn_left && can_turn_right)
6956       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6957     else if (can_turn_left)
6958       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6959     else if (can_turn_right)
6960       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6961     else
6962       MovDir[x][y] = back_dir;
6963
6964     MovDelay[x][y] = 16 + 16 * RND(3);
6965   }
6966   else if (element == EL_DARK_YAMYAM)
6967   {
6968     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6969                                                          left_x, left_y);
6970     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6971                                                          right_x, right_y);
6972
6973     if (can_turn_left && can_turn_right)
6974       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6975     else if (can_turn_left)
6976       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6977     else if (can_turn_right)
6978       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6979     else
6980       MovDir[x][y] = back_dir;
6981
6982     MovDelay[x][y] = 16 + 16 * RND(3);
6983   }
6984   else if (element == EL_PACMAN)
6985   {
6986     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6987     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6988
6989     if (can_turn_left && can_turn_right)
6990       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6991     else if (can_turn_left)
6992       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6993     else if (can_turn_right)
6994       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6995     else
6996       MovDir[x][y] = back_dir;
6997
6998     MovDelay[x][y] = 6 + RND(40);
6999   }
7000   else if (element == EL_PIG)
7001   {
7002     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7003     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7004     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7005     boolean should_turn_left, should_turn_right, should_move_on;
7006     int rnd_value = 24;
7007     int rnd = RND(rnd_value);
7008
7009     should_turn_left = (can_turn_left &&
7010                         (!can_move_on ||
7011                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7012                                                    y + back_dy + left_dy)));
7013     should_turn_right = (can_turn_right &&
7014                          (!can_move_on ||
7015                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7016                                                     y + back_dy + right_dy)));
7017     should_move_on = (can_move_on &&
7018                       (!can_turn_left ||
7019                        !can_turn_right ||
7020                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7021                                                  y + move_dy + left_dy) ||
7022                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7023                                                  y + move_dy + right_dy)));
7024
7025     if (should_turn_left || should_turn_right || should_move_on)
7026     {
7027       if (should_turn_left && should_turn_right && should_move_on)
7028         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7029                         rnd < 2 * rnd_value / 3 ? right_dir :
7030                         old_move_dir);
7031       else if (should_turn_left && should_turn_right)
7032         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7033       else if (should_turn_left && should_move_on)
7034         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7035       else if (should_turn_right && should_move_on)
7036         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7037       else if (should_turn_left)
7038         MovDir[x][y] = left_dir;
7039       else if (should_turn_right)
7040         MovDir[x][y] = right_dir;
7041       else if (should_move_on)
7042         MovDir[x][y] = old_move_dir;
7043     }
7044     else if (can_move_on && rnd > rnd_value / 8)
7045       MovDir[x][y] = old_move_dir;
7046     else if (can_turn_left && can_turn_right)
7047       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7048     else if (can_turn_left && rnd > rnd_value / 8)
7049       MovDir[x][y] = left_dir;
7050     else if (can_turn_right && rnd > rnd_value/8)
7051       MovDir[x][y] = right_dir;
7052     else
7053       MovDir[x][y] = back_dir;
7054
7055     xx = x + move_xy[MovDir[x][y]].dx;
7056     yy = y + move_xy[MovDir[x][y]].dy;
7057
7058     if (!IN_LEV_FIELD(xx, yy) ||
7059         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7060       MovDir[x][y] = old_move_dir;
7061
7062     MovDelay[x][y] = 0;
7063   }
7064   else if (element == EL_DRAGON)
7065   {
7066     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7067     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7068     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7069     int rnd_value = 24;
7070     int rnd = RND(rnd_value);
7071
7072     if (can_move_on && rnd > rnd_value / 8)
7073       MovDir[x][y] = old_move_dir;
7074     else if (can_turn_left && can_turn_right)
7075       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7076     else if (can_turn_left && rnd > rnd_value / 8)
7077       MovDir[x][y] = left_dir;
7078     else if (can_turn_right && rnd > rnd_value / 8)
7079       MovDir[x][y] = right_dir;
7080     else
7081       MovDir[x][y] = back_dir;
7082
7083     xx = x + move_xy[MovDir[x][y]].dx;
7084     yy = y + move_xy[MovDir[x][y]].dy;
7085
7086     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7087       MovDir[x][y] = old_move_dir;
7088
7089     MovDelay[x][y] = 0;
7090   }
7091   else if (element == EL_MOLE)
7092   {
7093     boolean can_move_on =
7094       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7095                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7096                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7097     if (!can_move_on)
7098     {
7099       boolean can_turn_left =
7100         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7101                               IS_AMOEBOID(Feld[left_x][left_y])));
7102
7103       boolean can_turn_right =
7104         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7105                               IS_AMOEBOID(Feld[right_x][right_y])));
7106
7107       if (can_turn_left && can_turn_right)
7108         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7109       else if (can_turn_left)
7110         MovDir[x][y] = left_dir;
7111       else
7112         MovDir[x][y] = right_dir;
7113     }
7114
7115     if (MovDir[x][y] != old_move_dir)
7116       MovDelay[x][y] = 9;
7117   }
7118   else if (element == EL_BALLOON)
7119   {
7120     MovDir[x][y] = game.wind_direction;
7121     MovDelay[x][y] = 0;
7122   }
7123   else if (element == EL_SPRING)
7124   {
7125 #if USE_NEW_SPRING_BUMPER
7126     if (MovDir[x][y] & MV_HORIZONTAL)
7127     {
7128       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7129           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7130       {
7131         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7132         ResetGfxAnimation(move_x, move_y);
7133         TEST_DrawLevelField(move_x, move_y);
7134
7135         MovDir[x][y] = back_dir;
7136       }
7137       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7138                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7139         MovDir[x][y] = MV_NONE;
7140     }
7141 #else
7142     if (MovDir[x][y] & MV_HORIZONTAL &&
7143         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7144          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7145       MovDir[x][y] = MV_NONE;
7146 #endif
7147
7148     MovDelay[x][y] = 0;
7149   }
7150   else if (element == EL_ROBOT ||
7151            element == EL_SATELLITE ||
7152            element == EL_PENGUIN ||
7153            element == EL_EMC_ANDROID)
7154   {
7155     int attr_x = -1, attr_y = -1;
7156
7157     if (AllPlayersGone)
7158     {
7159       attr_x = ExitX;
7160       attr_y = ExitY;
7161     }
7162     else
7163     {
7164       int i;
7165
7166       for (i = 0; i < MAX_PLAYERS; i++)
7167       {
7168         struct PlayerInfo *player = &stored_player[i];
7169         int jx = player->jx, jy = player->jy;
7170
7171         if (!player->active)
7172           continue;
7173
7174         if (attr_x == -1 ||
7175             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7176         {
7177           attr_x = jx;
7178           attr_y = jy;
7179         }
7180       }
7181     }
7182
7183     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7184         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7185          game.engine_version < VERSION_IDENT(3,1,0,0)))
7186     {
7187       attr_x = ZX;
7188       attr_y = ZY;
7189     }
7190
7191     if (element == EL_PENGUIN)
7192     {
7193       int i;
7194       static int xy[4][2] =
7195       {
7196         { 0, -1 },
7197         { -1, 0 },
7198         { +1, 0 },
7199         { 0, +1 }
7200       };
7201
7202       for (i = 0; i < NUM_DIRECTIONS; i++)
7203       {
7204         int ex = x + xy[i][0];
7205         int ey = y + xy[i][1];
7206
7207         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7208                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7209                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7210                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7211         {
7212           attr_x = ex;
7213           attr_y = ey;
7214           break;
7215         }
7216       }
7217     }
7218
7219     MovDir[x][y] = MV_NONE;
7220     if (attr_x < x)
7221       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7222     else if (attr_x > x)
7223       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7224     if (attr_y < y)
7225       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7226     else if (attr_y > y)
7227       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7228
7229     if (element == EL_ROBOT)
7230     {
7231       int newx, newy;
7232
7233       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7234         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7235       Moving2Blocked(x, y, &newx, &newy);
7236
7237       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7238         MovDelay[x][y] = 8 + 8 * !RND(3);
7239       else
7240         MovDelay[x][y] = 16;
7241     }
7242     else if (element == EL_PENGUIN)
7243     {
7244       int newx, newy;
7245
7246       MovDelay[x][y] = 1;
7247
7248       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7249       {
7250         boolean first_horiz = RND(2);
7251         int new_move_dir = MovDir[x][y];
7252
7253         MovDir[x][y] =
7254           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7255         Moving2Blocked(x, y, &newx, &newy);
7256
7257         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7258           return;
7259
7260         MovDir[x][y] =
7261           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7262         Moving2Blocked(x, y, &newx, &newy);
7263
7264         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7265           return;
7266
7267         MovDir[x][y] = old_move_dir;
7268         return;
7269       }
7270     }
7271     else if (element == EL_SATELLITE)
7272     {
7273       int newx, newy;
7274
7275       MovDelay[x][y] = 1;
7276
7277       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7278       {
7279         boolean first_horiz = RND(2);
7280         int new_move_dir = MovDir[x][y];
7281
7282         MovDir[x][y] =
7283           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7284         Moving2Blocked(x, y, &newx, &newy);
7285
7286         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7287           return;
7288
7289         MovDir[x][y] =
7290           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7291         Moving2Blocked(x, y, &newx, &newy);
7292
7293         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7294           return;
7295
7296         MovDir[x][y] = old_move_dir;
7297         return;
7298       }
7299     }
7300     else if (element == EL_EMC_ANDROID)
7301     {
7302       static int check_pos[16] =
7303       {
7304         -1,             /*  0 => (invalid)          */
7305         7,              /*  1 => MV_LEFT            */
7306         3,              /*  2 => MV_RIGHT           */
7307         -1,             /*  3 => (invalid)          */
7308         1,              /*  4 =>            MV_UP   */
7309         0,              /*  5 => MV_LEFT  | MV_UP   */
7310         2,              /*  6 => MV_RIGHT | MV_UP   */
7311         -1,             /*  7 => (invalid)          */
7312         5,              /*  8 =>            MV_DOWN */
7313         6,              /*  9 => MV_LEFT  | MV_DOWN */
7314         4,              /* 10 => MV_RIGHT | MV_DOWN */
7315         -1,             /* 11 => (invalid)          */
7316         -1,             /* 12 => (invalid)          */
7317         -1,             /* 13 => (invalid)          */
7318         -1,             /* 14 => (invalid)          */
7319         -1,             /* 15 => (invalid)          */
7320       };
7321       static struct
7322       {
7323         int dx, dy;
7324         int dir;
7325       } check_xy[8] =
7326       {
7327         { -1, -1,       MV_LEFT  | MV_UP   },
7328         {  0, -1,                  MV_UP   },
7329         { +1, -1,       MV_RIGHT | MV_UP   },
7330         { +1,  0,       MV_RIGHT           },
7331         { +1, +1,       MV_RIGHT | MV_DOWN },
7332         {  0, +1,                  MV_DOWN },
7333         { -1, +1,       MV_LEFT  | MV_DOWN },
7334         { -1,  0,       MV_LEFT            },
7335       };
7336       int start_pos, check_order;
7337       boolean can_clone = FALSE;
7338       int i;
7339
7340       /* check if there is any free field around current position */
7341       for (i = 0; i < 8; i++)
7342       {
7343         int newx = x + check_xy[i].dx;
7344         int newy = y + check_xy[i].dy;
7345
7346         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7347         {
7348           can_clone = TRUE;
7349
7350           break;
7351         }
7352       }
7353
7354       if (can_clone)            /* randomly find an element to clone */
7355       {
7356         can_clone = FALSE;
7357
7358         start_pos = check_pos[RND(8)];
7359         check_order = (RND(2) ? -1 : +1);
7360
7361         for (i = 0; i < 8; i++)
7362         {
7363           int pos_raw = start_pos + i * check_order;
7364           int pos = (pos_raw + 8) % 8;
7365           int newx = x + check_xy[pos].dx;
7366           int newy = y + check_xy[pos].dy;
7367
7368           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7369           {
7370             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7371             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7372
7373             Store[x][y] = Feld[newx][newy];
7374
7375             can_clone = TRUE;
7376
7377             break;
7378           }
7379         }
7380       }
7381
7382       if (can_clone)            /* randomly find a direction to move */
7383       {
7384         can_clone = FALSE;
7385
7386         start_pos = check_pos[RND(8)];
7387         check_order = (RND(2) ? -1 : +1);
7388
7389         for (i = 0; i < 8; i++)
7390         {
7391           int pos_raw = start_pos + i * check_order;
7392           int pos = (pos_raw + 8) % 8;
7393           int newx = x + check_xy[pos].dx;
7394           int newy = y + check_xy[pos].dy;
7395           int new_move_dir = check_xy[pos].dir;
7396
7397           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7398           {
7399             MovDir[x][y] = new_move_dir;
7400             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7401
7402             can_clone = TRUE;
7403
7404             break;
7405           }
7406         }
7407       }
7408
7409       if (can_clone)            /* cloning and moving successful */
7410         return;
7411
7412       /* cannot clone -- try to move towards player */
7413
7414       start_pos = check_pos[MovDir[x][y] & 0x0f];
7415       check_order = (RND(2) ? -1 : +1);
7416
7417       for (i = 0; i < 3; i++)
7418       {
7419         /* first check start_pos, then previous/next or (next/previous) pos */
7420         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7421         int pos = (pos_raw + 8) % 8;
7422         int newx = x + check_xy[pos].dx;
7423         int newy = y + check_xy[pos].dy;
7424         int new_move_dir = check_xy[pos].dir;
7425
7426         if (IS_PLAYER(newx, newy))
7427           break;
7428
7429         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7430         {
7431           MovDir[x][y] = new_move_dir;
7432           MovDelay[x][y] = level.android_move_time * 8 + 1;
7433
7434           break;
7435         }
7436       }
7437     }
7438   }
7439   else if (move_pattern == MV_TURNING_LEFT ||
7440            move_pattern == MV_TURNING_RIGHT ||
7441            move_pattern == MV_TURNING_LEFT_RIGHT ||
7442            move_pattern == MV_TURNING_RIGHT_LEFT ||
7443            move_pattern == MV_TURNING_RANDOM ||
7444            move_pattern == MV_ALL_DIRECTIONS)
7445   {
7446     boolean can_turn_left =
7447       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7448     boolean can_turn_right =
7449       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7450
7451     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7452       return;
7453
7454     if (move_pattern == MV_TURNING_LEFT)
7455       MovDir[x][y] = left_dir;
7456     else if (move_pattern == MV_TURNING_RIGHT)
7457       MovDir[x][y] = right_dir;
7458     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7459       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7460     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7461       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7462     else if (move_pattern == MV_TURNING_RANDOM)
7463       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7464                       can_turn_right && !can_turn_left ? right_dir :
7465                       RND(2) ? left_dir : right_dir);
7466     else if (can_turn_left && can_turn_right)
7467       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7468     else if (can_turn_left)
7469       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7470     else if (can_turn_right)
7471       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7472     else
7473       MovDir[x][y] = back_dir;
7474
7475     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7476   }
7477   else if (move_pattern == MV_HORIZONTAL ||
7478            move_pattern == MV_VERTICAL)
7479   {
7480     if (move_pattern & old_move_dir)
7481       MovDir[x][y] = back_dir;
7482     else if (move_pattern == MV_HORIZONTAL)
7483       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7484     else if (move_pattern == MV_VERTICAL)
7485       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7486
7487     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7488   }
7489   else if (move_pattern & MV_ANY_DIRECTION)
7490   {
7491     MovDir[x][y] = move_pattern;
7492     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7493   }
7494   else if (move_pattern & MV_WIND_DIRECTION)
7495   {
7496     MovDir[x][y] = game.wind_direction;
7497     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7498   }
7499   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7500   {
7501     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7502       MovDir[x][y] = left_dir;
7503     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7504       MovDir[x][y] = right_dir;
7505
7506     if (MovDir[x][y] != old_move_dir)
7507       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7508   }
7509   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7510   {
7511     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7512       MovDir[x][y] = right_dir;
7513     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7514       MovDir[x][y] = left_dir;
7515
7516     if (MovDir[x][y] != old_move_dir)
7517       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7518   }
7519   else if (move_pattern == MV_TOWARDS_PLAYER ||
7520            move_pattern == MV_AWAY_FROM_PLAYER)
7521   {
7522     int attr_x = -1, attr_y = -1;
7523     int newx, newy;
7524     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7525
7526     if (AllPlayersGone)
7527     {
7528       attr_x = ExitX;
7529       attr_y = ExitY;
7530     }
7531     else
7532     {
7533       int i;
7534
7535       for (i = 0; i < MAX_PLAYERS; i++)
7536       {
7537         struct PlayerInfo *player = &stored_player[i];
7538         int jx = player->jx, jy = player->jy;
7539
7540         if (!player->active)
7541           continue;
7542
7543         if (attr_x == -1 ||
7544             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7545         {
7546           attr_x = jx;
7547           attr_y = jy;
7548         }
7549       }
7550     }
7551
7552     MovDir[x][y] = MV_NONE;
7553     if (attr_x < x)
7554       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7555     else if (attr_x > x)
7556       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7557     if (attr_y < y)
7558       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7559     else if (attr_y > y)
7560       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7561
7562     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7563
7564     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7565     {
7566       boolean first_horiz = RND(2);
7567       int new_move_dir = MovDir[x][y];
7568
7569       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7570       {
7571         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7572         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7573
7574         return;
7575       }
7576
7577       MovDir[x][y] =
7578         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7579       Moving2Blocked(x, y, &newx, &newy);
7580
7581       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7582         return;
7583
7584       MovDir[x][y] =
7585         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7586       Moving2Blocked(x, y, &newx, &newy);
7587
7588       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7589         return;
7590
7591       MovDir[x][y] = old_move_dir;
7592     }
7593   }
7594   else if (move_pattern == MV_WHEN_PUSHED ||
7595            move_pattern == MV_WHEN_DROPPED)
7596   {
7597     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7598       MovDir[x][y] = MV_NONE;
7599
7600     MovDelay[x][y] = 0;
7601   }
7602   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7603   {
7604     static int test_xy[7][2] =
7605     {
7606       { 0, -1 },
7607       { -1, 0 },
7608       { +1, 0 },
7609       { 0, +1 },
7610       { 0, -1 },
7611       { -1, 0 },
7612       { +1, 0 },
7613     };
7614     static int test_dir[7] =
7615     {
7616       MV_UP,
7617       MV_LEFT,
7618       MV_RIGHT,
7619       MV_DOWN,
7620       MV_UP,
7621       MV_LEFT,
7622       MV_RIGHT,
7623     };
7624     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7625     int move_preference = -1000000;     /* start with very low preference */
7626     int new_move_dir = MV_NONE;
7627     int start_test = RND(4);
7628     int i;
7629
7630     for (i = 0; i < NUM_DIRECTIONS; i++)
7631     {
7632       int move_dir = test_dir[start_test + i];
7633       int move_dir_preference;
7634
7635       xx = x + test_xy[start_test + i][0];
7636       yy = y + test_xy[start_test + i][1];
7637
7638       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7639           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7640       {
7641         new_move_dir = move_dir;
7642
7643         break;
7644       }
7645
7646       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7647         continue;
7648
7649       move_dir_preference = -1 * RunnerVisit[xx][yy];
7650       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7651         move_dir_preference = PlayerVisit[xx][yy];
7652
7653       if (move_dir_preference > move_preference)
7654       {
7655         /* prefer field that has not been visited for the longest time */
7656         move_preference = move_dir_preference;
7657         new_move_dir = move_dir;
7658       }
7659       else if (move_dir_preference == move_preference &&
7660                move_dir == old_move_dir)
7661       {
7662         /* prefer last direction when all directions are preferred equally */
7663         move_preference = move_dir_preference;
7664         new_move_dir = move_dir;
7665       }
7666     }
7667
7668     MovDir[x][y] = new_move_dir;
7669     if (old_move_dir != new_move_dir)
7670       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7671   }
7672 }
7673
7674 static void TurnRound(int x, int y)
7675 {
7676   int direction = MovDir[x][y];
7677
7678   TurnRoundExt(x, y);
7679
7680   GfxDir[x][y] = MovDir[x][y];
7681
7682   if (direction != MovDir[x][y])
7683     GfxFrame[x][y] = 0;
7684
7685   if (MovDelay[x][y])
7686     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7687
7688   ResetGfxFrame(x, y, FALSE);
7689 }
7690
7691 static boolean JustBeingPushed(int x, int y)
7692 {
7693   int i;
7694
7695   for (i = 0; i < MAX_PLAYERS; i++)
7696   {
7697     struct PlayerInfo *player = &stored_player[i];
7698
7699     if (player->active && player->is_pushing && player->MovPos)
7700     {
7701       int next_jx = player->jx + (player->jx - player->last_jx);
7702       int next_jy = player->jy + (player->jy - player->last_jy);
7703
7704       if (x == next_jx && y == next_jy)
7705         return TRUE;
7706     }
7707   }
7708
7709   return FALSE;
7710 }
7711
7712 void StartMoving(int x, int y)
7713 {
7714   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7715   int element = Feld[x][y];
7716
7717   if (Stop[x][y])
7718     return;
7719
7720   if (MovDelay[x][y] == 0)
7721     GfxAction[x][y] = ACTION_DEFAULT;
7722
7723   if (CAN_FALL(element) && y < lev_fieldy - 1)
7724   {
7725     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7726         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7727       if (JustBeingPushed(x, y))
7728         return;
7729
7730     if (element == EL_QUICKSAND_FULL)
7731     {
7732       if (IS_FREE(x, y + 1))
7733       {
7734         InitMovingField(x, y, MV_DOWN);
7735         started_moving = TRUE;
7736
7737         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7738 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7739         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7740           Store[x][y] = EL_ROCK;
7741 #else
7742         Store[x][y] = EL_ROCK;
7743 #endif
7744
7745         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7746       }
7747       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7748       {
7749         if (!MovDelay[x][y])
7750         {
7751           MovDelay[x][y] = TILEY + 1;
7752
7753           ResetGfxAnimation(x, y);
7754           ResetGfxAnimation(x, y + 1);
7755         }
7756
7757         if (MovDelay[x][y])
7758         {
7759           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7760           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7761
7762           MovDelay[x][y]--;
7763           if (MovDelay[x][y])
7764             return;
7765         }
7766
7767         Feld[x][y] = EL_QUICKSAND_EMPTY;
7768         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7769         Store[x][y + 1] = Store[x][y];
7770         Store[x][y] = 0;
7771
7772         PlayLevelSoundAction(x, y, ACTION_FILLING);
7773       }
7774       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7775       {
7776         if (!MovDelay[x][y])
7777         {
7778           MovDelay[x][y] = TILEY + 1;
7779
7780           ResetGfxAnimation(x, y);
7781           ResetGfxAnimation(x, y + 1);
7782         }
7783
7784         if (MovDelay[x][y])
7785         {
7786           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7787           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7788
7789           MovDelay[x][y]--;
7790           if (MovDelay[x][y])
7791             return;
7792         }
7793
7794         Feld[x][y] = EL_QUICKSAND_EMPTY;
7795         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7796         Store[x][y + 1] = Store[x][y];
7797         Store[x][y] = 0;
7798
7799         PlayLevelSoundAction(x, y, ACTION_FILLING);
7800       }
7801     }
7802     else if (element == EL_QUICKSAND_FAST_FULL)
7803     {
7804       if (IS_FREE(x, y + 1))
7805       {
7806         InitMovingField(x, y, MV_DOWN);
7807         started_moving = TRUE;
7808
7809         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7810 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7811         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7812           Store[x][y] = EL_ROCK;
7813 #else
7814         Store[x][y] = EL_ROCK;
7815 #endif
7816
7817         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7818       }
7819       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7820       {
7821         if (!MovDelay[x][y])
7822         {
7823           MovDelay[x][y] = TILEY + 1;
7824
7825           ResetGfxAnimation(x, y);
7826           ResetGfxAnimation(x, y + 1);
7827         }
7828
7829         if (MovDelay[x][y])
7830         {
7831           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7832           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7833
7834           MovDelay[x][y]--;
7835           if (MovDelay[x][y])
7836             return;
7837         }
7838
7839         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7840         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7841         Store[x][y + 1] = Store[x][y];
7842         Store[x][y] = 0;
7843
7844         PlayLevelSoundAction(x, y, ACTION_FILLING);
7845       }
7846       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7847       {
7848         if (!MovDelay[x][y])
7849         {
7850           MovDelay[x][y] = TILEY + 1;
7851
7852           ResetGfxAnimation(x, y);
7853           ResetGfxAnimation(x, y + 1);
7854         }
7855
7856         if (MovDelay[x][y])
7857         {
7858           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7859           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7860
7861           MovDelay[x][y]--;
7862           if (MovDelay[x][y])
7863             return;
7864         }
7865
7866         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7867         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7868         Store[x][y + 1] = Store[x][y];
7869         Store[x][y] = 0;
7870
7871         PlayLevelSoundAction(x, y, ACTION_FILLING);
7872       }
7873     }
7874     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7875              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7876     {
7877       InitMovingField(x, y, MV_DOWN);
7878       started_moving = TRUE;
7879
7880       Feld[x][y] = EL_QUICKSAND_FILLING;
7881       Store[x][y] = element;
7882
7883       PlayLevelSoundAction(x, y, ACTION_FILLING);
7884     }
7885     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7886              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887     {
7888       InitMovingField(x, y, MV_DOWN);
7889       started_moving = TRUE;
7890
7891       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7892       Store[x][y] = element;
7893
7894       PlayLevelSoundAction(x, y, ACTION_FILLING);
7895     }
7896     else if (element == EL_MAGIC_WALL_FULL)
7897     {
7898       if (IS_FREE(x, y + 1))
7899       {
7900         InitMovingField(x, y, MV_DOWN);
7901         started_moving = TRUE;
7902
7903         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7904         Store[x][y] = EL_CHANGED(Store[x][y]);
7905       }
7906       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7907       {
7908         if (!MovDelay[x][y])
7909           MovDelay[x][y] = TILEY/4 + 1;
7910
7911         if (MovDelay[x][y])
7912         {
7913           MovDelay[x][y]--;
7914           if (MovDelay[x][y])
7915             return;
7916         }
7917
7918         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7919         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7920         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7921         Store[x][y] = 0;
7922       }
7923     }
7924     else if (element == EL_BD_MAGIC_WALL_FULL)
7925     {
7926       if (IS_FREE(x, y + 1))
7927       {
7928         InitMovingField(x, y, MV_DOWN);
7929         started_moving = TRUE;
7930
7931         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7932         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7933       }
7934       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7935       {
7936         if (!MovDelay[x][y])
7937           MovDelay[x][y] = TILEY/4 + 1;
7938
7939         if (MovDelay[x][y])
7940         {
7941           MovDelay[x][y]--;
7942           if (MovDelay[x][y])
7943             return;
7944         }
7945
7946         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7947         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7948         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7949         Store[x][y] = 0;
7950       }
7951     }
7952     else if (element == EL_DC_MAGIC_WALL_FULL)
7953     {
7954       if (IS_FREE(x, y + 1))
7955       {
7956         InitMovingField(x, y, MV_DOWN);
7957         started_moving = TRUE;
7958
7959         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7960         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7961       }
7962       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7963       {
7964         if (!MovDelay[x][y])
7965           MovDelay[x][y] = TILEY/4 + 1;
7966
7967         if (MovDelay[x][y])
7968         {
7969           MovDelay[x][y]--;
7970           if (MovDelay[x][y])
7971             return;
7972         }
7973
7974         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7975         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7976         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7977         Store[x][y] = 0;
7978       }
7979     }
7980     else if ((CAN_PASS_MAGIC_WALL(element) &&
7981               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7982                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7983              (CAN_PASS_DC_MAGIC_WALL(element) &&
7984               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7985
7986     {
7987       InitMovingField(x, y, MV_DOWN);
7988       started_moving = TRUE;
7989
7990       Feld[x][y] =
7991         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7992          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7993          EL_DC_MAGIC_WALL_FILLING);
7994       Store[x][y] = element;
7995     }
7996     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7997     {
7998       SplashAcid(x, y + 1);
7999
8000       InitMovingField(x, y, MV_DOWN);
8001       started_moving = TRUE;
8002
8003       Store[x][y] = EL_ACID;
8004     }
8005     else if (
8006 #if USE_FIX_IMPACT_COLLISION
8007              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8008               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8009 #else
8010              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8011               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8012 #endif
8013              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8014               CAN_FALL(element) && WasJustFalling[x][y] &&
8015               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8016
8017              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8018               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8019               (Feld[x][y + 1] == EL_BLOCKED)))
8020     {
8021       /* this is needed for a special case not covered by calling "Impact()"
8022          from "ContinueMoving()": if an element moves to a tile directly below
8023          another element which was just falling on that tile (which was empty
8024          in the previous frame), the falling element above would just stop
8025          instead of smashing the element below (in previous version, the above
8026          element was just checked for "moving" instead of "falling", resulting
8027          in incorrect smashes caused by horizontal movement of the above
8028          element; also, the case of the player being the element to smash was
8029          simply not covered here... :-/ ) */
8030
8031       CheckCollision[x][y] = 0;
8032       CheckImpact[x][y] = 0;
8033
8034       Impact(x, y);
8035     }
8036     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8037     {
8038       if (MovDir[x][y] == MV_NONE)
8039       {
8040         InitMovingField(x, y, MV_DOWN);
8041         started_moving = TRUE;
8042       }
8043     }
8044     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8045     {
8046       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8047         MovDir[x][y] = MV_DOWN;
8048
8049       InitMovingField(x, y, MV_DOWN);
8050       started_moving = TRUE;
8051     }
8052     else if (element == EL_AMOEBA_DROP)
8053     {
8054       Feld[x][y] = EL_AMOEBA_GROWING;
8055       Store[x][y] = EL_AMOEBA_WET;
8056     }
8057     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8058               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8059              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8060              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8061     {
8062       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8063                                 (IS_FREE(x - 1, y + 1) ||
8064                                  Feld[x - 1][y + 1] == EL_ACID));
8065       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8066                                 (IS_FREE(x + 1, y + 1) ||
8067                                  Feld[x + 1][y + 1] == EL_ACID));
8068       boolean can_fall_any  = (can_fall_left || can_fall_right);
8069       boolean can_fall_both = (can_fall_left && can_fall_right);
8070       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8071
8072 #if USE_NEW_ALL_SLIPPERY
8073       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8074       {
8075         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8076           can_fall_right = FALSE;
8077         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8078           can_fall_left = FALSE;
8079         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8080           can_fall_right = FALSE;
8081         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8082           can_fall_left = FALSE;
8083
8084         can_fall_any  = (can_fall_left || can_fall_right);
8085         can_fall_both = FALSE;
8086       }
8087 #else
8088       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8089       {
8090         if (slippery_type == SLIPPERY_ONLY_LEFT)
8091           can_fall_right = FALSE;
8092         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8093           can_fall_left = FALSE;
8094         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8095           can_fall_right = FALSE;
8096         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8097           can_fall_left = FALSE;
8098
8099         can_fall_any  = (can_fall_left || can_fall_right);
8100         can_fall_both = (can_fall_left && can_fall_right);
8101       }
8102 #endif
8103
8104 #if USE_NEW_ALL_SLIPPERY
8105 #else
8106 #if USE_NEW_SP_SLIPPERY
8107       /* !!! better use the same properties as for custom elements here !!! */
8108       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8109                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8110       {
8111         can_fall_right = FALSE;         /* slip down on left side */
8112         can_fall_both = FALSE;
8113       }
8114 #endif
8115 #endif
8116
8117 #if USE_NEW_ALL_SLIPPERY
8118       if (can_fall_both)
8119       {
8120         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8121           can_fall_right = FALSE;       /* slip down on left side */
8122         else
8123           can_fall_left = !(can_fall_right = RND(2));
8124
8125         can_fall_both = FALSE;
8126       }
8127 #else
8128       if (can_fall_both)
8129       {
8130         if (game.emulation == EMU_BOULDERDASH ||
8131             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8132           can_fall_right = FALSE;       /* slip down on left side */
8133         else
8134           can_fall_left = !(can_fall_right = RND(2));
8135
8136         can_fall_both = FALSE;
8137       }
8138 #endif
8139
8140       if (can_fall_any)
8141       {
8142         /* if not determined otherwise, prefer left side for slipping down */
8143         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8144         started_moving = TRUE;
8145       }
8146     }
8147 #if 0
8148     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8149 #else
8150     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8151 #endif
8152     {
8153       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8154       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8155       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8156       int belt_dir = game.belt_dir[belt_nr];
8157
8158       if ((belt_dir == MV_LEFT  && left_is_free) ||
8159           (belt_dir == MV_RIGHT && right_is_free))
8160       {
8161         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8162
8163         InitMovingField(x, y, belt_dir);
8164         started_moving = TRUE;
8165
8166         Pushed[x][y] = TRUE;
8167         Pushed[nextx][y] = TRUE;
8168
8169         GfxAction[x][y] = ACTION_DEFAULT;
8170       }
8171       else
8172       {
8173         MovDir[x][y] = 0;       /* if element was moving, stop it */
8174       }
8175     }
8176   }
8177
8178   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8179 #if 0
8180   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8181 #else
8182   if (CAN_MOVE(element) && !started_moving)
8183 #endif
8184   {
8185     int move_pattern = element_info[element].move_pattern;
8186     int newx, newy;
8187
8188 #if 0
8189 #if DEBUG
8190     if (MovDir[x][y] == MV_NONE)
8191     {
8192       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8193              x, y, element, element_info[element].token_name);
8194       printf("StartMoving(): This should never happen!\n");
8195     }
8196 #endif
8197 #endif
8198
8199     Moving2Blocked(x, y, &newx, &newy);
8200
8201     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8202       return;
8203
8204     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8205         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8206     {
8207       WasJustMoving[x][y] = 0;
8208       CheckCollision[x][y] = 0;
8209
8210       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8211
8212       if (Feld[x][y] != element)        /* element has changed */
8213         return;
8214     }
8215
8216     if (!MovDelay[x][y])        /* start new movement phase */
8217     {
8218       /* all objects that can change their move direction after each step
8219          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8220
8221       if (element != EL_YAMYAM &&
8222           element != EL_DARK_YAMYAM &&
8223           element != EL_PACMAN &&
8224           !(move_pattern & MV_ANY_DIRECTION) &&
8225           move_pattern != MV_TURNING_LEFT &&
8226           move_pattern != MV_TURNING_RIGHT &&
8227           move_pattern != MV_TURNING_LEFT_RIGHT &&
8228           move_pattern != MV_TURNING_RIGHT_LEFT &&
8229           move_pattern != MV_TURNING_RANDOM)
8230       {
8231         TurnRound(x, y);
8232
8233         if (MovDelay[x][y] && (element == EL_BUG ||
8234                                element == EL_SPACESHIP ||
8235                                element == EL_SP_SNIKSNAK ||
8236                                element == EL_SP_ELECTRON ||
8237                                element == EL_MOLE))
8238           TEST_DrawLevelField(x, y);
8239       }
8240     }
8241
8242     if (MovDelay[x][y])         /* wait some time before next movement */
8243     {
8244       MovDelay[x][y]--;
8245
8246       if (element == EL_ROBOT ||
8247           element == EL_YAMYAM ||
8248           element == EL_DARK_YAMYAM)
8249       {
8250         DrawLevelElementAnimationIfNeeded(x, y, element);
8251         PlayLevelSoundAction(x, y, ACTION_WAITING);
8252       }
8253       else if (element == EL_SP_ELECTRON)
8254         DrawLevelElementAnimationIfNeeded(x, y, element);
8255       else if (element == EL_DRAGON)
8256       {
8257         int i;
8258         int dir = MovDir[x][y];
8259         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8260         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8261         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8262                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8263                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8264                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8265         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8266
8267         GfxAction[x][y] = ACTION_ATTACKING;
8268
8269         if (IS_PLAYER(x, y))
8270           DrawPlayerField(x, y);
8271         else
8272           TEST_DrawLevelField(x, y);
8273
8274         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8275
8276         for (i = 1; i <= 3; i++)
8277         {
8278           int xx = x + i * dx;
8279           int yy = y + i * dy;
8280           int sx = SCREENX(xx);
8281           int sy = SCREENY(yy);
8282           int flame_graphic = graphic + (i - 1);
8283
8284           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8285             break;
8286
8287           if (MovDelay[x][y])
8288           {
8289             int flamed = MovingOrBlocked2Element(xx, yy);
8290
8291             /* !!! */
8292 #if 0
8293             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8294               Bang(xx, yy);
8295             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8296               RemoveMovingField(xx, yy);
8297             else
8298               RemoveField(xx, yy);
8299 #else
8300             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8301               Bang(xx, yy);
8302             else
8303               RemoveMovingField(xx, yy);
8304 #endif
8305
8306             ChangeDelay[xx][yy] = 0;
8307
8308             Feld[xx][yy] = EL_FLAMES;
8309
8310             if (IN_SCR_FIELD(sx, sy))
8311             {
8312               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8313               DrawGraphic(sx, sy, flame_graphic, frame);
8314             }
8315           }
8316           else
8317           {
8318             if (Feld[xx][yy] == EL_FLAMES)
8319               Feld[xx][yy] = EL_EMPTY;
8320             TEST_DrawLevelField(xx, yy);
8321           }
8322         }
8323       }
8324
8325       if (MovDelay[x][y])       /* element still has to wait some time */
8326       {
8327         PlayLevelSoundAction(x, y, ACTION_WAITING);
8328
8329         return;
8330       }
8331     }
8332
8333     /* now make next step */
8334
8335     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8336
8337     if (DONT_COLLIDE_WITH(element) &&
8338         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8339         !PLAYER_ENEMY_PROTECTED(newx, newy))
8340     {
8341       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8342
8343       return;
8344     }
8345
8346     else if (CAN_MOVE_INTO_ACID(element) &&
8347              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8348              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8349              (MovDir[x][y] == MV_DOWN ||
8350               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8351     {
8352       SplashAcid(newx, newy);
8353       Store[x][y] = EL_ACID;
8354     }
8355     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8356     {
8357       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8358           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8359           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8360           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8361       {
8362         RemoveField(x, y);
8363         TEST_DrawLevelField(x, y);
8364
8365         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8366         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8367           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8368
8369         local_player->friends_still_needed--;
8370         if (!local_player->friends_still_needed &&
8371             !local_player->GameOver && AllPlayersGone)
8372           PlayerWins(local_player);
8373
8374         return;
8375       }
8376       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8377       {
8378         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8379           TEST_DrawLevelField(newx, newy);
8380         else
8381           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8382       }
8383       else if (!IS_FREE(newx, newy))
8384       {
8385         GfxAction[x][y] = ACTION_WAITING;
8386
8387         if (IS_PLAYER(x, y))
8388           DrawPlayerField(x, y);
8389         else
8390           TEST_DrawLevelField(x, y);
8391
8392         return;
8393       }
8394     }
8395     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8396     {
8397       if (IS_FOOD_PIG(Feld[newx][newy]))
8398       {
8399         if (IS_MOVING(newx, newy))
8400           RemoveMovingField(newx, newy);
8401         else
8402         {
8403           Feld[newx][newy] = EL_EMPTY;
8404           TEST_DrawLevelField(newx, newy);
8405         }
8406
8407         PlayLevelSound(x, y, SND_PIG_DIGGING);
8408       }
8409       else if (!IS_FREE(newx, newy))
8410       {
8411         if (IS_PLAYER(x, y))
8412           DrawPlayerField(x, y);
8413         else
8414           TEST_DrawLevelField(x, y);
8415
8416         return;
8417       }
8418     }
8419     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8420     {
8421       if (Store[x][y] != EL_EMPTY)
8422       {
8423         boolean can_clone = FALSE;
8424         int xx, yy;
8425
8426         /* check if element to clone is still there */
8427         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8428         {
8429           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8430           {
8431             can_clone = TRUE;
8432
8433             break;
8434           }
8435         }
8436
8437         /* cannot clone or target field not free anymore -- do not clone */
8438         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8439           Store[x][y] = EL_EMPTY;
8440       }
8441
8442       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8443       {
8444         if (IS_MV_DIAGONAL(MovDir[x][y]))
8445         {
8446           int diagonal_move_dir = MovDir[x][y];
8447           int stored = Store[x][y];
8448           int change_delay = 8;
8449           int graphic;
8450
8451           /* android is moving diagonally */
8452
8453           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8454
8455           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8456           GfxElement[x][y] = EL_EMC_ANDROID;
8457           GfxAction[x][y] = ACTION_SHRINKING;
8458           GfxDir[x][y] = diagonal_move_dir;
8459           ChangeDelay[x][y] = change_delay;
8460
8461           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8462                                    GfxDir[x][y]);
8463
8464           DrawLevelGraphicAnimation(x, y, graphic);
8465           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8466
8467           if (Feld[newx][newy] == EL_ACID)
8468           {
8469             SplashAcid(newx, newy);
8470
8471             return;
8472           }
8473
8474           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8475
8476           Store[newx][newy] = EL_EMC_ANDROID;
8477           GfxElement[newx][newy] = EL_EMC_ANDROID;
8478           GfxAction[newx][newy] = ACTION_GROWING;
8479           GfxDir[newx][newy] = diagonal_move_dir;
8480           ChangeDelay[newx][newy] = change_delay;
8481
8482           graphic = el_act_dir2img(GfxElement[newx][newy],
8483                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8484
8485           DrawLevelGraphicAnimation(newx, newy, graphic);
8486           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8487
8488           return;
8489         }
8490         else
8491         {
8492           Feld[newx][newy] = EL_EMPTY;
8493           TEST_DrawLevelField(newx, newy);
8494
8495           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8496         }
8497       }
8498       else if (!IS_FREE(newx, newy))
8499       {
8500 #if 0
8501         if (IS_PLAYER(x, y))
8502           DrawPlayerField(x, y);
8503         else
8504           TEST_DrawLevelField(x, y);
8505 #endif
8506
8507         return;
8508       }
8509     }
8510     else if (IS_CUSTOM_ELEMENT(element) &&
8511              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8512     {
8513 #if 1
8514       if (!DigFieldByCE(newx, newy, element))
8515         return;
8516 #else
8517       int new_element = Feld[newx][newy];
8518
8519       if (!IS_FREE(newx, newy))
8520       {
8521         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8522                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8523                       ACTION_BREAKING);
8524
8525         /* no element can dig solid indestructible elements */
8526         if (IS_INDESTRUCTIBLE(new_element) &&
8527             !IS_DIGGABLE(new_element) &&
8528             !IS_COLLECTIBLE(new_element))
8529           return;
8530
8531         if (AmoebaNr[newx][newy] &&
8532             (new_element == EL_AMOEBA_FULL ||
8533              new_element == EL_BD_AMOEBA ||
8534              new_element == EL_AMOEBA_GROWING))
8535         {
8536           AmoebaCnt[AmoebaNr[newx][newy]]--;
8537           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8538         }
8539
8540         if (IS_MOVING(newx, newy))
8541           RemoveMovingField(newx, newy);
8542         else
8543         {
8544           RemoveField(newx, newy);
8545           TEST_DrawLevelField(newx, newy);
8546         }
8547
8548         /* if digged element was about to explode, prevent the explosion */
8549         ExplodeField[newx][newy] = EX_TYPE_NONE;
8550
8551         PlayLevelSoundAction(x, y, action);
8552       }
8553
8554       Store[newx][newy] = EL_EMPTY;
8555
8556 #if 1
8557       /* this makes it possible to leave the removed element again */
8558       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8559         Store[newx][newy] = new_element;
8560 #else
8561       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8562       {
8563         int move_leave_element = element_info[element].move_leave_element;
8564
8565         /* this makes it possible to leave the removed element again */
8566         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8567                              new_element : move_leave_element);
8568       }
8569 #endif
8570
8571 #endif
8572
8573       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8574       {
8575         RunnerVisit[x][y] = FrameCounter;
8576         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8577       }
8578     }
8579     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8580     {
8581       if (!IS_FREE(newx, newy))
8582       {
8583         if (IS_PLAYER(x, y))
8584           DrawPlayerField(x, y);
8585         else
8586           TEST_DrawLevelField(x, y);
8587
8588         return;
8589       }
8590       else
8591       {
8592         boolean wanna_flame = !RND(10);
8593         int dx = newx - x, dy = newy - y;
8594         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8595         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8596         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8597                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8598         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8599                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8600
8601         if ((wanna_flame ||
8602              IS_CLASSIC_ENEMY(element1) ||
8603              IS_CLASSIC_ENEMY(element2)) &&
8604             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8605             element1 != EL_FLAMES && element2 != EL_FLAMES)
8606         {
8607           ResetGfxAnimation(x, y);
8608           GfxAction[x][y] = ACTION_ATTACKING;
8609
8610           if (IS_PLAYER(x, y))
8611             DrawPlayerField(x, y);
8612           else
8613             TEST_DrawLevelField(x, y);
8614
8615           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8616
8617           MovDelay[x][y] = 50;
8618
8619           /* !!! */
8620 #if 0
8621           RemoveField(newx, newy);
8622 #endif
8623           Feld[newx][newy] = EL_FLAMES;
8624           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8625           {
8626 #if 0
8627             RemoveField(newx1, newy1);
8628 #endif
8629             Feld[newx1][newy1] = EL_FLAMES;
8630           }
8631           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8632           {
8633 #if 0
8634             RemoveField(newx2, newy2);
8635 #endif
8636             Feld[newx2][newy2] = EL_FLAMES;
8637           }
8638
8639           return;
8640         }
8641       }
8642     }
8643     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8644              Feld[newx][newy] == EL_DIAMOND)
8645     {
8646       if (IS_MOVING(newx, newy))
8647         RemoveMovingField(newx, newy);
8648       else
8649       {
8650         Feld[newx][newy] = EL_EMPTY;
8651         TEST_DrawLevelField(newx, newy);
8652       }
8653
8654       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8655     }
8656     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8657              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8658     {
8659       if (AmoebaNr[newx][newy])
8660       {
8661         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8662         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8663             Feld[newx][newy] == EL_BD_AMOEBA)
8664           AmoebaCnt[AmoebaNr[newx][newy]]--;
8665       }
8666
8667 #if 0
8668       /* !!! test !!! */
8669       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8670       {
8671         RemoveMovingField(newx, newy);
8672       }
8673 #else
8674       if (IS_MOVING(newx, newy))
8675       {
8676         RemoveMovingField(newx, newy);
8677       }
8678 #endif
8679       else
8680       {
8681         Feld[newx][newy] = EL_EMPTY;
8682         TEST_DrawLevelField(newx, newy);
8683       }
8684
8685       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8686     }
8687     else if ((element == EL_PACMAN || element == EL_MOLE)
8688              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8689     {
8690       if (AmoebaNr[newx][newy])
8691       {
8692         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8693         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8694             Feld[newx][newy] == EL_BD_AMOEBA)
8695           AmoebaCnt[AmoebaNr[newx][newy]]--;
8696       }
8697
8698       if (element == EL_MOLE)
8699       {
8700         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8701         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8702
8703         ResetGfxAnimation(x, y);
8704         GfxAction[x][y] = ACTION_DIGGING;
8705         TEST_DrawLevelField(x, y);
8706
8707         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8708
8709         return;                         /* wait for shrinking amoeba */
8710       }
8711       else      /* element == EL_PACMAN */
8712       {
8713         Feld[newx][newy] = EL_EMPTY;
8714         TEST_DrawLevelField(newx, newy);
8715         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8716       }
8717     }
8718     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8719              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8720               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8721     {
8722       /* wait for shrinking amoeba to completely disappear */
8723       return;
8724     }
8725     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8726     {
8727       /* object was running against a wall */
8728
8729       TurnRound(x, y);
8730
8731 #if 0
8732       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8733       if (move_pattern & MV_ANY_DIRECTION &&
8734           move_pattern == MovDir[x][y])
8735       {
8736         int blocking_element =
8737           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8738
8739         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8740                                  MovDir[x][y]);
8741
8742         element = Feld[x][y];   /* element might have changed */
8743       }
8744 #endif
8745
8746       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8747         DrawLevelElementAnimation(x, y, element);
8748
8749       if (DONT_TOUCH(element))
8750         TestIfBadThingTouchesPlayer(x, y);
8751
8752       return;
8753     }
8754
8755     InitMovingField(x, y, MovDir[x][y]);
8756
8757     PlayLevelSoundAction(x, y, ACTION_MOVING);
8758   }
8759
8760   if (MovDir[x][y])
8761     ContinueMoving(x, y);
8762 }
8763
8764 void ContinueMoving(int x, int y)
8765 {
8766   int element = Feld[x][y];
8767   struct ElementInfo *ei = &element_info[element];
8768   int direction = MovDir[x][y];
8769   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8770   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8771   int newx = x + dx, newy = y + dy;
8772   int stored = Store[x][y];
8773   int stored_new = Store[newx][newy];
8774   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8775   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8776   boolean last_line = (newy == lev_fieldy - 1);
8777
8778   MovPos[x][y] += getElementMoveStepsize(x, y);
8779
8780   if (pushed_by_player) /* special case: moving object pushed by player */
8781     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8782
8783   if (ABS(MovPos[x][y]) < TILEX)
8784   {
8785 #if 0
8786     int ee = Feld[x][y];
8787     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8788     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8789
8790     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8791            x, y, ABS(MovPos[x][y]),
8792            ee, gg, ff,
8793            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8794 #endif
8795
8796     TEST_DrawLevelField(x, y);
8797
8798     return;     /* element is still moving */
8799   }
8800
8801   /* element reached destination field */
8802
8803   Feld[x][y] = EL_EMPTY;
8804   Feld[newx][newy] = element;
8805   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8806
8807   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8808   {
8809     element = Feld[newx][newy] = EL_ACID;
8810   }
8811   else if (element == EL_MOLE)
8812   {
8813     Feld[x][y] = EL_SAND;
8814
8815     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8816   }
8817   else if (element == EL_QUICKSAND_FILLING)
8818   {
8819     element = Feld[newx][newy] = get_next_element(element);
8820     Store[newx][newy] = Store[x][y];
8821   }
8822   else if (element == EL_QUICKSAND_EMPTYING)
8823   {
8824     Feld[x][y] = get_next_element(element);
8825     element = Feld[newx][newy] = Store[x][y];
8826   }
8827   else if (element == EL_QUICKSAND_FAST_FILLING)
8828   {
8829     element = Feld[newx][newy] = get_next_element(element);
8830     Store[newx][newy] = Store[x][y];
8831   }
8832   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8833   {
8834     Feld[x][y] = get_next_element(element);
8835     element = Feld[newx][newy] = Store[x][y];
8836   }
8837   else if (element == EL_MAGIC_WALL_FILLING)
8838   {
8839     element = Feld[newx][newy] = get_next_element(element);
8840     if (!game.magic_wall_active)
8841       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8842     Store[newx][newy] = Store[x][y];
8843   }
8844   else if (element == EL_MAGIC_WALL_EMPTYING)
8845   {
8846     Feld[x][y] = get_next_element(element);
8847     if (!game.magic_wall_active)
8848       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8849     element = Feld[newx][newy] = Store[x][y];
8850
8851 #if USE_NEW_CUSTOM_VALUE
8852     InitField(newx, newy, FALSE);
8853 #endif
8854   }
8855   else if (element == EL_BD_MAGIC_WALL_FILLING)
8856   {
8857     element = Feld[newx][newy] = get_next_element(element);
8858     if (!game.magic_wall_active)
8859       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8860     Store[newx][newy] = Store[x][y];
8861   }
8862   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8863   {
8864     Feld[x][y] = get_next_element(element);
8865     if (!game.magic_wall_active)
8866       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8867     element = Feld[newx][newy] = Store[x][y];
8868
8869 #if USE_NEW_CUSTOM_VALUE
8870     InitField(newx, newy, FALSE);
8871 #endif
8872   }
8873   else if (element == EL_DC_MAGIC_WALL_FILLING)
8874   {
8875     element = Feld[newx][newy] = get_next_element(element);
8876     if (!game.magic_wall_active)
8877       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8878     Store[newx][newy] = Store[x][y];
8879   }
8880   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8881   {
8882     Feld[x][y] = get_next_element(element);
8883     if (!game.magic_wall_active)
8884       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8885     element = Feld[newx][newy] = Store[x][y];
8886
8887 #if USE_NEW_CUSTOM_VALUE
8888     InitField(newx, newy, FALSE);
8889 #endif
8890   }
8891   else if (element == EL_AMOEBA_DROPPING)
8892   {
8893     Feld[x][y] = get_next_element(element);
8894     element = Feld[newx][newy] = Store[x][y];
8895   }
8896   else if (element == EL_SOKOBAN_OBJECT)
8897   {
8898     if (Back[x][y])
8899       Feld[x][y] = Back[x][y];
8900
8901     if (Back[newx][newy])
8902       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8903
8904     Back[x][y] = Back[newx][newy] = 0;
8905   }
8906
8907   Store[x][y] = EL_EMPTY;
8908   MovPos[x][y] = 0;
8909   MovDir[x][y] = 0;
8910   MovDelay[x][y] = 0;
8911
8912   MovDelay[newx][newy] = 0;
8913
8914   if (CAN_CHANGE_OR_HAS_ACTION(element))
8915   {
8916     /* copy element change control values to new field */
8917     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8918     ChangePage[newx][newy]  = ChangePage[x][y];
8919     ChangeCount[newx][newy] = ChangeCount[x][y];
8920     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8921   }
8922
8923 #if USE_NEW_CUSTOM_VALUE
8924   CustomValue[newx][newy] = CustomValue[x][y];
8925 #endif
8926
8927   ChangeDelay[x][y] = 0;
8928   ChangePage[x][y] = -1;
8929   ChangeCount[x][y] = 0;
8930   ChangeEvent[x][y] = -1;
8931
8932 #if USE_NEW_CUSTOM_VALUE
8933   CustomValue[x][y] = 0;
8934 #endif
8935
8936   /* copy animation control values to new field */
8937   GfxFrame[newx][newy]  = GfxFrame[x][y];
8938   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8939   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8940   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8941
8942   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8943
8944   /* some elements can leave other elements behind after moving */
8945 #if 1
8946   if (ei->move_leave_element != EL_EMPTY &&
8947       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8948       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8949 #else
8950   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8951       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8952       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8953 #endif
8954   {
8955     int move_leave_element = ei->move_leave_element;
8956
8957 #if 1
8958 #if 1
8959     /* this makes it possible to leave the removed element again */
8960     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8961       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8962 #else
8963     /* this makes it possible to leave the removed element again */
8964     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8965       move_leave_element = stored;
8966 #endif
8967 #else
8968     /* this makes it possible to leave the removed element again */
8969     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8970         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8971       move_leave_element = stored;
8972 #endif
8973
8974     Feld[x][y] = move_leave_element;
8975
8976     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8977       MovDir[x][y] = direction;
8978
8979     InitField(x, y, FALSE);
8980
8981     if (GFX_CRUMBLED(Feld[x][y]))
8982       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8983
8984     if (ELEM_IS_PLAYER(move_leave_element))
8985       RelocatePlayer(x, y, move_leave_element);
8986   }
8987
8988   /* do this after checking for left-behind element */
8989   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8990
8991   if (!CAN_MOVE(element) ||
8992       (CAN_FALL(element) && direction == MV_DOWN &&
8993        (element == EL_SPRING ||
8994         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8995         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8996     GfxDir[x][y] = MovDir[newx][newy] = 0;
8997
8998   TEST_DrawLevelField(x, y);
8999   TEST_DrawLevelField(newx, newy);
9000
9001   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9002
9003   /* prevent pushed element from moving on in pushed direction */
9004   if (pushed_by_player && CAN_MOVE(element) &&
9005       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9006       !(element_info[element].move_pattern & direction))
9007     TurnRound(newx, newy);
9008
9009   /* prevent elements on conveyor belt from moving on in last direction */
9010   if (pushed_by_conveyor && CAN_FALL(element) &&
9011       direction & MV_HORIZONTAL)
9012     MovDir[newx][newy] = 0;
9013
9014   if (!pushed_by_player)
9015   {
9016     int nextx = newx + dx, nexty = newy + dy;
9017     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9018
9019     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9020
9021     if (CAN_FALL(element) && direction == MV_DOWN)
9022       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9023
9024     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9025       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9026
9027 #if USE_FIX_IMPACT_COLLISION
9028     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9029       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9030 #endif
9031   }
9032
9033   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9034   {
9035     TestIfBadThingTouchesPlayer(newx, newy);
9036     TestIfBadThingTouchesFriend(newx, newy);
9037
9038     if (!IS_CUSTOM_ELEMENT(element))
9039       TestIfBadThingTouchesOtherBadThing(newx, newy);
9040   }
9041   else if (element == EL_PENGUIN)
9042     TestIfFriendTouchesBadThing(newx, newy);
9043
9044   if (DONT_GET_HIT_BY(element))
9045   {
9046     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9047   }
9048
9049   /* give the player one last chance (one more frame) to move away */
9050   if (CAN_FALL(element) && direction == MV_DOWN &&
9051       (last_line || (!IS_FREE(x, newy + 1) &&
9052                      (!IS_PLAYER(x, newy + 1) ||
9053                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9054     Impact(x, newy);
9055
9056   if (pushed_by_player && !game.use_change_when_pushing_bug)
9057   {
9058     int push_side = MV_DIR_OPPOSITE(direction);
9059     struct PlayerInfo *player = PLAYERINFO(x, y);
9060
9061     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9062                                player->index_bit, push_side);
9063     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9064                                         player->index_bit, push_side);
9065   }
9066
9067   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9068     MovDelay[newx][newy] = 1;
9069
9070   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9071
9072   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9073
9074 #if 0
9075   if (ChangePage[newx][newy] != -1)             /* delayed change */
9076   {
9077     int page = ChangePage[newx][newy];
9078     struct ElementChangeInfo *change = &ei->change_page[page];
9079
9080     ChangePage[newx][newy] = -1;
9081
9082     if (change->can_change)
9083     {
9084       if (ChangeElement(newx, newy, element, page))
9085       {
9086         if (change->post_change_function)
9087           change->post_change_function(newx, newy);
9088       }
9089     }
9090
9091     if (change->has_action)
9092       ExecuteCustomElementAction(newx, newy, element, page);
9093   }
9094 #endif
9095
9096   TestIfElementHitsCustomElement(newx, newy, direction);
9097   TestIfPlayerTouchesCustomElement(newx, newy);
9098   TestIfElementTouchesCustomElement(newx, newy);
9099
9100   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9101       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9102     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9103                              MV_DIR_OPPOSITE(direction));
9104 }
9105
9106 int AmoebeNachbarNr(int ax, int ay)
9107 {
9108   int i;
9109   int element = Feld[ax][ay];
9110   int group_nr = 0;
9111   static int xy[4][2] =
9112   {
9113     { 0, -1 },
9114     { -1, 0 },
9115     { +1, 0 },
9116     { 0, +1 }
9117   };
9118
9119   for (i = 0; i < NUM_DIRECTIONS; i++)
9120   {
9121     int x = ax + xy[i][0];
9122     int y = ay + xy[i][1];
9123
9124     if (!IN_LEV_FIELD(x, y))
9125       continue;
9126
9127     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9128       group_nr = AmoebaNr[x][y];
9129   }
9130
9131   return group_nr;
9132 }
9133
9134 void AmoebenVereinigen(int ax, int ay)
9135 {
9136   int i, x, y, xx, yy;
9137   int new_group_nr = AmoebaNr[ax][ay];
9138   static int xy[4][2] =
9139   {
9140     { 0, -1 },
9141     { -1, 0 },
9142     { +1, 0 },
9143     { 0, +1 }
9144   };
9145
9146   if (new_group_nr == 0)
9147     return;
9148
9149   for (i = 0; i < NUM_DIRECTIONS; i++)
9150   {
9151     x = ax + xy[i][0];
9152     y = ay + xy[i][1];
9153
9154     if (!IN_LEV_FIELD(x, y))
9155       continue;
9156
9157     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9158          Feld[x][y] == EL_BD_AMOEBA ||
9159          Feld[x][y] == EL_AMOEBA_DEAD) &&
9160         AmoebaNr[x][y] != new_group_nr)
9161     {
9162       int old_group_nr = AmoebaNr[x][y];
9163
9164       if (old_group_nr == 0)
9165         return;
9166
9167       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9168       AmoebaCnt[old_group_nr] = 0;
9169       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9170       AmoebaCnt2[old_group_nr] = 0;
9171
9172       SCAN_PLAYFIELD(xx, yy)
9173       {
9174         if (AmoebaNr[xx][yy] == old_group_nr)
9175           AmoebaNr[xx][yy] = new_group_nr;
9176       }
9177     }
9178   }
9179 }
9180
9181 void AmoebeUmwandeln(int ax, int ay)
9182 {
9183   int i, x, y;
9184
9185   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9186   {
9187     int group_nr = AmoebaNr[ax][ay];
9188
9189 #ifdef DEBUG
9190     if (group_nr == 0)
9191     {
9192       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9193       printf("AmoebeUmwandeln(): This should never happen!\n");
9194       return;
9195     }
9196 #endif
9197
9198     SCAN_PLAYFIELD(x, y)
9199     {
9200       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9201       {
9202         AmoebaNr[x][y] = 0;
9203         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9204       }
9205     }
9206
9207     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9208                             SND_AMOEBA_TURNING_TO_GEM :
9209                             SND_AMOEBA_TURNING_TO_ROCK));
9210     Bang(ax, ay);
9211   }
9212   else
9213   {
9214     static int xy[4][2] =
9215     {
9216       { 0, -1 },
9217       { -1, 0 },
9218       { +1, 0 },
9219       { 0, +1 }
9220     };
9221
9222     for (i = 0; i < NUM_DIRECTIONS; i++)
9223     {
9224       x = ax + xy[i][0];
9225       y = ay + xy[i][1];
9226
9227       if (!IN_LEV_FIELD(x, y))
9228         continue;
9229
9230       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9231       {
9232         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9233                               SND_AMOEBA_TURNING_TO_GEM :
9234                               SND_AMOEBA_TURNING_TO_ROCK));
9235         Bang(x, y);
9236       }
9237     }
9238   }
9239 }
9240
9241 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9242 {
9243   int x, y;
9244   int group_nr = AmoebaNr[ax][ay];
9245   boolean done = FALSE;
9246
9247 #ifdef DEBUG
9248   if (group_nr == 0)
9249   {
9250     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9251     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9252     return;
9253   }
9254 #endif
9255
9256   SCAN_PLAYFIELD(x, y)
9257   {
9258     if (AmoebaNr[x][y] == group_nr &&
9259         (Feld[x][y] == EL_AMOEBA_DEAD ||
9260          Feld[x][y] == EL_BD_AMOEBA ||
9261          Feld[x][y] == EL_AMOEBA_GROWING))
9262     {
9263       AmoebaNr[x][y] = 0;
9264       Feld[x][y] = new_element;
9265       InitField(x, y, FALSE);
9266       TEST_DrawLevelField(x, y);
9267       done = TRUE;
9268     }
9269   }
9270
9271   if (done)
9272     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9273                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9274                             SND_BD_AMOEBA_TURNING_TO_GEM));
9275 }
9276
9277 void AmoebeWaechst(int x, int y)
9278 {
9279   static unsigned long sound_delay = 0;
9280   static unsigned long sound_delay_value = 0;
9281
9282   if (!MovDelay[x][y])          /* start new growing cycle */
9283   {
9284     MovDelay[x][y] = 7;
9285
9286     if (DelayReached(&sound_delay, sound_delay_value))
9287     {
9288       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9289       sound_delay_value = 30;
9290     }
9291   }
9292
9293   if (MovDelay[x][y])           /* wait some time before growing bigger */
9294   {
9295     MovDelay[x][y]--;
9296     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9297     {
9298       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9299                                            6 - MovDelay[x][y]);
9300
9301       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9302     }
9303
9304     if (!MovDelay[x][y])
9305     {
9306       Feld[x][y] = Store[x][y];
9307       Store[x][y] = 0;
9308       TEST_DrawLevelField(x, y);
9309     }
9310   }
9311 }
9312
9313 void AmoebaDisappearing(int x, int y)
9314 {
9315   static unsigned long sound_delay = 0;
9316   static unsigned long sound_delay_value = 0;
9317
9318   if (!MovDelay[x][y])          /* start new shrinking cycle */
9319   {
9320     MovDelay[x][y] = 7;
9321
9322     if (DelayReached(&sound_delay, sound_delay_value))
9323       sound_delay_value = 30;
9324   }
9325
9326   if (MovDelay[x][y])           /* wait some time before shrinking */
9327   {
9328     MovDelay[x][y]--;
9329     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9330     {
9331       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9332                                            6 - MovDelay[x][y]);
9333
9334       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9335     }
9336
9337     if (!MovDelay[x][y])
9338     {
9339       Feld[x][y] = EL_EMPTY;
9340       TEST_DrawLevelField(x, y);
9341
9342       /* don't let mole enter this field in this cycle;
9343          (give priority to objects falling to this field from above) */
9344       Stop[x][y] = TRUE;
9345     }
9346   }
9347 }
9348
9349 void AmoebeAbleger(int ax, int ay)
9350 {
9351   int i;
9352   int element = Feld[ax][ay];
9353   int graphic = el2img(element);
9354   int newax = ax, neway = ay;
9355   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9356   static int xy[4][2] =
9357   {
9358     { 0, -1 },
9359     { -1, 0 },
9360     { +1, 0 },
9361     { 0, +1 }
9362   };
9363
9364   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9365   {
9366     Feld[ax][ay] = EL_AMOEBA_DEAD;
9367     TEST_DrawLevelField(ax, ay);
9368     return;
9369   }
9370
9371   if (IS_ANIMATED(graphic))
9372     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9373
9374   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9375     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9376
9377   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9378   {
9379     MovDelay[ax][ay]--;
9380     if (MovDelay[ax][ay])
9381       return;
9382   }
9383
9384   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9385   {
9386     int start = RND(4);
9387     int x = ax + xy[start][0];
9388     int y = ay + xy[start][1];
9389
9390     if (!IN_LEV_FIELD(x, y))
9391       return;
9392
9393     if (IS_FREE(x, y) ||
9394         CAN_GROW_INTO(Feld[x][y]) ||
9395         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9396         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9397     {
9398       newax = x;
9399       neway = y;
9400     }
9401
9402     if (newax == ax && neway == ay)
9403       return;
9404   }
9405   else                          /* normal or "filled" (BD style) amoeba */
9406   {
9407     int start = RND(4);
9408     boolean waiting_for_player = FALSE;
9409
9410     for (i = 0; i < NUM_DIRECTIONS; i++)
9411     {
9412       int j = (start + i) % 4;
9413       int x = ax + xy[j][0];
9414       int y = ay + xy[j][1];
9415
9416       if (!IN_LEV_FIELD(x, y))
9417         continue;
9418
9419       if (IS_FREE(x, y) ||
9420           CAN_GROW_INTO(Feld[x][y]) ||
9421           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9422           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9423       {
9424         newax = x;
9425         neway = y;
9426         break;
9427       }
9428       else if (IS_PLAYER(x, y))
9429         waiting_for_player = TRUE;
9430     }
9431
9432     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9433     {
9434       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9435       {
9436         Feld[ax][ay] = EL_AMOEBA_DEAD;
9437         TEST_DrawLevelField(ax, ay);
9438         AmoebaCnt[AmoebaNr[ax][ay]]--;
9439
9440         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9441         {
9442           if (element == EL_AMOEBA_FULL)
9443             AmoebeUmwandeln(ax, ay);
9444           else if (element == EL_BD_AMOEBA)
9445             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9446         }
9447       }
9448       return;
9449     }
9450     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9451     {
9452       /* amoeba gets larger by growing in some direction */
9453
9454       int new_group_nr = AmoebaNr[ax][ay];
9455
9456 #ifdef DEBUG
9457   if (new_group_nr == 0)
9458   {
9459     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9460     printf("AmoebeAbleger(): This should never happen!\n");
9461     return;
9462   }
9463 #endif
9464
9465       AmoebaNr[newax][neway] = new_group_nr;
9466       AmoebaCnt[new_group_nr]++;
9467       AmoebaCnt2[new_group_nr]++;
9468
9469       /* if amoeba touches other amoeba(s) after growing, unify them */
9470       AmoebenVereinigen(newax, neway);
9471
9472       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9473       {
9474         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9475         return;
9476       }
9477     }
9478   }
9479
9480   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9481       (neway == lev_fieldy - 1 && newax != ax))
9482   {
9483     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9484     Store[newax][neway] = element;
9485   }
9486   else if (neway == ay || element == EL_EMC_DRIPPER)
9487   {
9488     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9489
9490     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9491   }
9492   else
9493   {
9494     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9495     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9496     Store[ax][ay] = EL_AMOEBA_DROP;
9497     ContinueMoving(ax, ay);
9498     return;
9499   }
9500
9501   TEST_DrawLevelField(newax, neway);
9502 }
9503
9504 void Life(int ax, int ay)
9505 {
9506   int x1, y1, x2, y2;
9507   int life_time = 40;
9508   int element = Feld[ax][ay];
9509   int graphic = el2img(element);
9510   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9511                          level.biomaze);
9512   boolean changed = FALSE;
9513
9514   if (IS_ANIMATED(graphic))
9515     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9516
9517   if (Stop[ax][ay])
9518     return;
9519
9520   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9521     MovDelay[ax][ay] = life_time;
9522
9523   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9524   {
9525     MovDelay[ax][ay]--;
9526     if (MovDelay[ax][ay])
9527       return;
9528   }
9529
9530   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9531   {
9532     int xx = ax+x1, yy = ay+y1;
9533     int nachbarn = 0;
9534
9535     if (!IN_LEV_FIELD(xx, yy))
9536       continue;
9537
9538     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9539     {
9540       int x = xx+x2, y = yy+y2;
9541
9542       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9543         continue;
9544
9545       if (((Feld[x][y] == element ||
9546             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9547            !Stop[x][y]) ||
9548           (IS_FREE(x, y) && Stop[x][y]))
9549         nachbarn++;
9550     }
9551
9552     if (xx == ax && yy == ay)           /* field in the middle */
9553     {
9554       if (nachbarn < life_parameter[0] ||
9555           nachbarn > life_parameter[1])
9556       {
9557         Feld[xx][yy] = EL_EMPTY;
9558         if (!Stop[xx][yy])
9559           TEST_DrawLevelField(xx, yy);
9560         Stop[xx][yy] = TRUE;
9561         changed = TRUE;
9562       }
9563     }
9564     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9565     {                                   /* free border field */
9566       if (nachbarn >= life_parameter[2] &&
9567           nachbarn <= life_parameter[3])
9568       {
9569         Feld[xx][yy] = element;
9570         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9571         if (!Stop[xx][yy])
9572           TEST_DrawLevelField(xx, yy);
9573         Stop[xx][yy] = TRUE;
9574         changed = TRUE;
9575       }
9576     }
9577   }
9578
9579   if (changed)
9580     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9581                    SND_GAME_OF_LIFE_GROWING);
9582 }
9583
9584 static void InitRobotWheel(int x, int y)
9585 {
9586   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9587 }
9588
9589 static void RunRobotWheel(int x, int y)
9590 {
9591   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9592 }
9593
9594 static void StopRobotWheel(int x, int y)
9595 {
9596   if (ZX == x && ZY == y)
9597   {
9598     ZX = ZY = -1;
9599
9600     game.robot_wheel_active = FALSE;
9601   }
9602 }
9603
9604 static void InitTimegateWheel(int x, int y)
9605 {
9606   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9607 }
9608
9609 static void RunTimegateWheel(int x, int y)
9610 {
9611   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9612 }
9613
9614 static void InitMagicBallDelay(int x, int y)
9615 {
9616 #if 1
9617   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9618 #else
9619   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9620 #endif
9621 }
9622
9623 static void ActivateMagicBall(int bx, int by)
9624 {
9625   int x, y;
9626
9627   if (level.ball_random)
9628   {
9629     int pos_border = RND(8);    /* select one of the eight border elements */
9630     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9631     int xx = pos_content % 3;
9632     int yy = pos_content / 3;
9633
9634     x = bx - 1 + xx;
9635     y = by - 1 + yy;
9636
9637     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9638       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9639   }
9640   else
9641   {
9642     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9643     {
9644       int xx = x - bx + 1;
9645       int yy = y - by + 1;
9646
9647       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9648         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9649     }
9650   }
9651
9652   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9653 }
9654
9655 void CheckExit(int x, int y)
9656 {
9657   if (local_player->gems_still_needed > 0 ||
9658       local_player->sokobanfields_still_needed > 0 ||
9659       local_player->lights_still_needed > 0)
9660   {
9661     int element = Feld[x][y];
9662     int graphic = el2img(element);
9663
9664     if (IS_ANIMATED(graphic))
9665       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9666
9667     return;
9668   }
9669
9670   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9671     return;
9672
9673   Feld[x][y] = EL_EXIT_OPENING;
9674
9675   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9676 }
9677
9678 void CheckExitEM(int x, int y)
9679 {
9680   if (local_player->gems_still_needed > 0 ||
9681       local_player->sokobanfields_still_needed > 0 ||
9682       local_player->lights_still_needed > 0)
9683   {
9684     int element = Feld[x][y];
9685     int graphic = el2img(element);
9686
9687     if (IS_ANIMATED(graphic))
9688       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9689
9690     return;
9691   }
9692
9693   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9694     return;
9695
9696   Feld[x][y] = EL_EM_EXIT_OPENING;
9697
9698   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9699 }
9700
9701 void CheckExitSteel(int x, int y)
9702 {
9703   if (local_player->gems_still_needed > 0 ||
9704       local_player->sokobanfields_still_needed > 0 ||
9705       local_player->lights_still_needed > 0)
9706   {
9707     int element = Feld[x][y];
9708     int graphic = el2img(element);
9709
9710     if (IS_ANIMATED(graphic))
9711       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9712
9713     return;
9714   }
9715
9716   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9717     return;
9718
9719   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9720
9721   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9722 }
9723
9724 void CheckExitSteelEM(int x, int y)
9725 {
9726   if (local_player->gems_still_needed > 0 ||
9727       local_player->sokobanfields_still_needed > 0 ||
9728       local_player->lights_still_needed > 0)
9729   {
9730     int element = Feld[x][y];
9731     int graphic = el2img(element);
9732
9733     if (IS_ANIMATED(graphic))
9734       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9735
9736     return;
9737   }
9738
9739   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9740     return;
9741
9742   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9743
9744   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9745 }
9746
9747 void CheckExitSP(int x, int y)
9748 {
9749   if (local_player->gems_still_needed > 0)
9750   {
9751     int element = Feld[x][y];
9752     int graphic = el2img(element);
9753
9754     if (IS_ANIMATED(graphic))
9755       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9756
9757     return;
9758   }
9759
9760   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9761     return;
9762
9763   Feld[x][y] = EL_SP_EXIT_OPENING;
9764
9765   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9766 }
9767
9768 static void CloseAllOpenTimegates()
9769 {
9770   int x, y;
9771
9772   SCAN_PLAYFIELD(x, y)
9773   {
9774     int element = Feld[x][y];
9775
9776     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9777     {
9778       Feld[x][y] = EL_TIMEGATE_CLOSING;
9779
9780       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9781     }
9782   }
9783 }
9784
9785 void DrawTwinkleOnField(int x, int y)
9786 {
9787   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9788     return;
9789
9790   if (Feld[x][y] == EL_BD_DIAMOND)
9791     return;
9792
9793   if (MovDelay[x][y] == 0)      /* next animation frame */
9794     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9795
9796   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9797   {
9798     MovDelay[x][y]--;
9799
9800     DrawLevelElementAnimation(x, y, Feld[x][y]);
9801
9802     if (MovDelay[x][y] != 0)
9803     {
9804       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9805                                            10 - MovDelay[x][y]);
9806
9807       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9808     }
9809   }
9810 }
9811
9812 void MauerWaechst(int x, int y)
9813 {
9814   int delay = 6;
9815
9816   if (!MovDelay[x][y])          /* next animation frame */
9817     MovDelay[x][y] = 3 * delay;
9818
9819   if (MovDelay[x][y])           /* wait some time before next frame */
9820   {
9821     MovDelay[x][y]--;
9822
9823     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9824     {
9825       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9826       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9827
9828       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9829     }
9830
9831     if (!MovDelay[x][y])
9832     {
9833       if (MovDir[x][y] == MV_LEFT)
9834       {
9835         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9836           TEST_DrawLevelField(x - 1, y);
9837       }
9838       else if (MovDir[x][y] == MV_RIGHT)
9839       {
9840         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9841           TEST_DrawLevelField(x + 1, y);
9842       }
9843       else if (MovDir[x][y] == MV_UP)
9844       {
9845         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9846           TEST_DrawLevelField(x, y - 1);
9847       }
9848       else
9849       {
9850         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9851           TEST_DrawLevelField(x, y + 1);
9852       }
9853
9854       Feld[x][y] = Store[x][y];
9855       Store[x][y] = 0;
9856       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9857       TEST_DrawLevelField(x, y);
9858     }
9859   }
9860 }
9861
9862 void MauerAbleger(int ax, int ay)
9863 {
9864   int element = Feld[ax][ay];
9865   int graphic = el2img(element);
9866   boolean oben_frei = FALSE, unten_frei = FALSE;
9867   boolean links_frei = FALSE, rechts_frei = FALSE;
9868   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9869   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9870   boolean new_wall = FALSE;
9871
9872   if (IS_ANIMATED(graphic))
9873     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9874
9875   if (!MovDelay[ax][ay])        /* start building new wall */
9876     MovDelay[ax][ay] = 6;
9877
9878   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9879   {
9880     MovDelay[ax][ay]--;
9881     if (MovDelay[ax][ay])
9882       return;
9883   }
9884
9885   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9886     oben_frei = TRUE;
9887   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9888     unten_frei = TRUE;
9889   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9890     links_frei = TRUE;
9891   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9892     rechts_frei = TRUE;
9893
9894   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9895       element == EL_EXPANDABLE_WALL_ANY)
9896   {
9897     if (oben_frei)
9898     {
9899       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9900       Store[ax][ay-1] = element;
9901       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9902       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9903         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9904                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9905       new_wall = TRUE;
9906     }
9907     if (unten_frei)
9908     {
9909       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9910       Store[ax][ay+1] = element;
9911       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9912       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9913         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9914                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9915       new_wall = TRUE;
9916     }
9917   }
9918
9919   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9920       element == EL_EXPANDABLE_WALL_ANY ||
9921       element == EL_EXPANDABLE_WALL ||
9922       element == EL_BD_EXPANDABLE_WALL)
9923   {
9924     if (links_frei)
9925     {
9926       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9927       Store[ax-1][ay] = element;
9928       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9929       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9930         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9931                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9932       new_wall = TRUE;
9933     }
9934
9935     if (rechts_frei)
9936     {
9937       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9938       Store[ax+1][ay] = element;
9939       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9940       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9941         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9942                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9943       new_wall = TRUE;
9944     }
9945   }
9946
9947   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9948     TEST_DrawLevelField(ax, ay);
9949
9950   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9951     oben_massiv = TRUE;
9952   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9953     unten_massiv = TRUE;
9954   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9955     links_massiv = TRUE;
9956   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9957     rechts_massiv = TRUE;
9958
9959   if (((oben_massiv && unten_massiv) ||
9960        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9961        element == EL_EXPANDABLE_WALL) &&
9962       ((links_massiv && rechts_massiv) ||
9963        element == EL_EXPANDABLE_WALL_VERTICAL))
9964     Feld[ax][ay] = EL_WALL;
9965
9966   if (new_wall)
9967     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9968 }
9969
9970 void MauerAblegerStahl(int ax, int ay)
9971 {
9972   int element = Feld[ax][ay];
9973   int graphic = el2img(element);
9974   boolean oben_frei = FALSE, unten_frei = FALSE;
9975   boolean links_frei = FALSE, rechts_frei = FALSE;
9976   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9977   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9978   boolean new_wall = FALSE;
9979
9980   if (IS_ANIMATED(graphic))
9981     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9982
9983   if (!MovDelay[ax][ay])        /* start building new wall */
9984     MovDelay[ax][ay] = 6;
9985
9986   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9987   {
9988     MovDelay[ax][ay]--;
9989     if (MovDelay[ax][ay])
9990       return;
9991   }
9992
9993   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9994     oben_frei = TRUE;
9995   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9996     unten_frei = TRUE;
9997   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9998     links_frei = TRUE;
9999   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10000     rechts_frei = TRUE;
10001
10002   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10003       element == EL_EXPANDABLE_STEELWALL_ANY)
10004   {
10005     if (oben_frei)
10006     {
10007       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10008       Store[ax][ay-1] = element;
10009       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10010       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10011         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10012                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10013       new_wall = TRUE;
10014     }
10015     if (unten_frei)
10016     {
10017       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10018       Store[ax][ay+1] = element;
10019       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10020       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10021         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10022                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10023       new_wall = TRUE;
10024     }
10025   }
10026
10027   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10028       element == EL_EXPANDABLE_STEELWALL_ANY)
10029   {
10030     if (links_frei)
10031     {
10032       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10033       Store[ax-1][ay] = element;
10034       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10035       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10036         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10037                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10038       new_wall = TRUE;
10039     }
10040
10041     if (rechts_frei)
10042     {
10043       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10044       Store[ax+1][ay] = element;
10045       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10046       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10047         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10048                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10049       new_wall = TRUE;
10050     }
10051   }
10052
10053   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10054     oben_massiv = TRUE;
10055   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10056     unten_massiv = TRUE;
10057   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10058     links_massiv = TRUE;
10059   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10060     rechts_massiv = TRUE;
10061
10062   if (((oben_massiv && unten_massiv) ||
10063        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10064       ((links_massiv && rechts_massiv) ||
10065        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10066     Feld[ax][ay] = EL_STEELWALL;
10067
10068   if (new_wall)
10069     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10070 }
10071
10072 void CheckForDragon(int x, int y)
10073 {
10074   int i, j;
10075   boolean dragon_found = FALSE;
10076   static int xy[4][2] =
10077   {
10078     { 0, -1 },
10079     { -1, 0 },
10080     { +1, 0 },
10081     { 0, +1 }
10082   };
10083
10084   for (i = 0; i < NUM_DIRECTIONS; i++)
10085   {
10086     for (j = 0; j < 4; j++)
10087     {
10088       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10089
10090       if (IN_LEV_FIELD(xx, yy) &&
10091           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10092       {
10093         if (Feld[xx][yy] == EL_DRAGON)
10094           dragon_found = TRUE;
10095       }
10096       else
10097         break;
10098     }
10099   }
10100
10101   if (!dragon_found)
10102   {
10103     for (i = 0; i < NUM_DIRECTIONS; i++)
10104     {
10105       for (j = 0; j < 3; j++)
10106       {
10107         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10108   
10109         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10110         {
10111           Feld[xx][yy] = EL_EMPTY;
10112           TEST_DrawLevelField(xx, yy);
10113         }
10114         else
10115           break;
10116       }
10117     }
10118   }
10119 }
10120
10121 static void InitBuggyBase(int x, int y)
10122 {
10123   int element = Feld[x][y];
10124   int activating_delay = FRAMES_PER_SECOND / 4;
10125
10126   ChangeDelay[x][y] =
10127     (element == EL_SP_BUGGY_BASE ?
10128      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10129      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10130      activating_delay :
10131      element == EL_SP_BUGGY_BASE_ACTIVE ?
10132      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10133 }
10134
10135 static void WarnBuggyBase(int x, int y)
10136 {
10137   int i;
10138   static int xy[4][2] =
10139   {
10140     { 0, -1 },
10141     { -1, 0 },
10142     { +1, 0 },
10143     { 0, +1 }
10144   };
10145
10146   for (i = 0; i < NUM_DIRECTIONS; i++)
10147   {
10148     int xx = x + xy[i][0];
10149     int yy = y + xy[i][1];
10150
10151     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10152     {
10153       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10154
10155       break;
10156     }
10157   }
10158 }
10159
10160 static void InitTrap(int x, int y)
10161 {
10162   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10163 }
10164
10165 static void ActivateTrap(int x, int y)
10166 {
10167   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10168 }
10169
10170 static void ChangeActiveTrap(int x, int y)
10171 {
10172   int graphic = IMG_TRAP_ACTIVE;
10173
10174   /* if new animation frame was drawn, correct crumbled sand border */
10175   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10176     TEST_DrawLevelFieldCrumbledSand(x, y);
10177 }
10178
10179 static int getSpecialActionElement(int element, int number, int base_element)
10180 {
10181   return (element != EL_EMPTY ? element :
10182           number != -1 ? base_element + number - 1 :
10183           EL_EMPTY);
10184 }
10185
10186 static int getModifiedActionNumber(int value_old, int operator, int operand,
10187                                    int value_min, int value_max)
10188 {
10189   int value_new = (operator == CA_MODE_SET      ? operand :
10190                    operator == CA_MODE_ADD      ? value_old + operand :
10191                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10192                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10193                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10194                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10195                    value_old);
10196
10197   return (value_new < value_min ? value_min :
10198           value_new > value_max ? value_max :
10199           value_new);
10200 }
10201
10202 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10203 {
10204   struct ElementInfo *ei = &element_info[element];
10205   struct ElementChangeInfo *change = &ei->change_page[page];
10206   int target_element = change->target_element;
10207   int action_type = change->action_type;
10208   int action_mode = change->action_mode;
10209   int action_arg = change->action_arg;
10210   int action_element = change->action_element;
10211   int i;
10212
10213   if (!change->has_action)
10214     return;
10215
10216   /* ---------- determine action paramater values -------------------------- */
10217
10218   int level_time_value =
10219     (level.time > 0 ? TimeLeft :
10220      TimePlayed);
10221
10222   int action_arg_element_raw =
10223     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10224      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10225      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10226      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10227      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10228      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10229      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10230      EL_EMPTY);
10231   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10232
10233 #if 0
10234   if (action_arg_element_raw == EL_GROUP_START)
10235     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10236 #endif
10237
10238   int action_arg_direction =
10239     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10240      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10241      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10242      change->actual_trigger_side :
10243      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10244      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10245      MV_NONE);
10246
10247   int action_arg_number_min =
10248     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10249      CA_ARG_MIN);
10250
10251   int action_arg_number_max =
10252     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10253      action_type == CA_SET_LEVEL_GEMS ? 999 :
10254      action_type == CA_SET_LEVEL_TIME ? 9999 :
10255      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10256      action_type == CA_SET_CE_VALUE ? 9999 :
10257      action_type == CA_SET_CE_SCORE ? 9999 :
10258      CA_ARG_MAX);
10259
10260   int action_arg_number_reset =
10261     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10262      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10263      action_type == CA_SET_LEVEL_TIME ? level.time :
10264      action_type == CA_SET_LEVEL_SCORE ? 0 :
10265 #if USE_NEW_CUSTOM_VALUE
10266      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10267 #else
10268      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10269 #endif
10270      action_type == CA_SET_CE_SCORE ? 0 :
10271      0);
10272
10273   int action_arg_number =
10274     (action_arg <= CA_ARG_MAX ? action_arg :
10275      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10276      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10277      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10278      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10279      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10280      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10281 #if USE_NEW_CUSTOM_VALUE
10282      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10283 #else
10284      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10285 #endif
10286      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10287      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10288      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10289      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10290      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10291      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10292      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10293      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10294      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10295      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10296      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10297      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10298      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10299      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10300      -1);
10301
10302   int action_arg_number_old =
10303     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10304      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10305      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10306      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10307      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10308      0);
10309
10310   int action_arg_number_new =
10311     getModifiedActionNumber(action_arg_number_old,
10312                             action_mode, action_arg_number,
10313                             action_arg_number_min, action_arg_number_max);
10314
10315 #if 1
10316   int trigger_player_bits =
10317     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10318      change->actual_trigger_player_bits : change->trigger_player);
10319 #else
10320   int trigger_player_bits =
10321     (change->actual_trigger_player >= EL_PLAYER_1 &&
10322      change->actual_trigger_player <= EL_PLAYER_4 ?
10323      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10324      PLAYER_BITS_ANY);
10325 #endif
10326
10327   int action_arg_player_bits =
10328     (action_arg >= CA_ARG_PLAYER_1 &&
10329      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10330      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10331      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10332      PLAYER_BITS_ANY);
10333
10334   /* ---------- execute action  -------------------------------------------- */
10335
10336   switch (action_type)
10337   {
10338     case CA_NO_ACTION:
10339     {
10340       return;
10341     }
10342
10343     /* ---------- level actions  ------------------------------------------- */
10344
10345     case CA_RESTART_LEVEL:
10346     {
10347       game.restart_level = TRUE;
10348
10349       break;
10350     }
10351
10352     case CA_SHOW_ENVELOPE:
10353     {
10354       int element = getSpecialActionElement(action_arg_element,
10355                                             action_arg_number, EL_ENVELOPE_1);
10356
10357       if (IS_ENVELOPE(element))
10358         local_player->show_envelope = element;
10359
10360       break;
10361     }
10362
10363     case CA_SET_LEVEL_TIME:
10364     {
10365       if (level.time > 0)       /* only modify limited time value */
10366       {
10367         TimeLeft = action_arg_number_new;
10368
10369 #if 1
10370         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10371
10372         DisplayGameControlValues();
10373 #else
10374         DrawGameValue_Time(TimeLeft);
10375 #endif
10376
10377         if (!TimeLeft && setup.time_limit)
10378           for (i = 0; i < MAX_PLAYERS; i++)
10379             KillPlayer(&stored_player[i]);
10380       }
10381
10382       break;
10383     }
10384
10385     case CA_SET_LEVEL_SCORE:
10386     {
10387       local_player->score = action_arg_number_new;
10388
10389 #if 1
10390       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10391
10392       DisplayGameControlValues();
10393 #else
10394       DrawGameValue_Score(local_player->score);
10395 #endif
10396
10397       break;
10398     }
10399
10400     case CA_SET_LEVEL_GEMS:
10401     {
10402       local_player->gems_still_needed = action_arg_number_new;
10403
10404 #if 1
10405       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10406
10407       DisplayGameControlValues();
10408 #else
10409       DrawGameValue_Emeralds(local_player->gems_still_needed);
10410 #endif
10411
10412       break;
10413     }
10414
10415 #if !USE_PLAYER_GRAVITY
10416     case CA_SET_LEVEL_GRAVITY:
10417     {
10418       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10419                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10420                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10421                       game.gravity);
10422       break;
10423     }
10424 #endif
10425
10426     case CA_SET_LEVEL_WIND:
10427     {
10428       game.wind_direction = action_arg_direction;
10429
10430       break;
10431     }
10432
10433     /* ---------- player actions  ------------------------------------------ */
10434
10435     case CA_MOVE_PLAYER:
10436     {
10437       /* automatically move to the next field in specified direction */
10438       for (i = 0; i < MAX_PLAYERS; i++)
10439         if (trigger_player_bits & (1 << i))
10440           stored_player[i].programmed_action = action_arg_direction;
10441
10442       break;
10443     }
10444
10445     case CA_EXIT_PLAYER:
10446     {
10447       for (i = 0; i < MAX_PLAYERS; i++)
10448         if (action_arg_player_bits & (1 << i))
10449           PlayerWins(&stored_player[i]);
10450
10451       break;
10452     }
10453
10454     case CA_KILL_PLAYER:
10455     {
10456       for (i = 0; i < MAX_PLAYERS; i++)
10457         if (action_arg_player_bits & (1 << i))
10458           KillPlayer(&stored_player[i]);
10459
10460       break;
10461     }
10462
10463     case CA_SET_PLAYER_KEYS:
10464     {
10465       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10466       int element = getSpecialActionElement(action_arg_element,
10467                                             action_arg_number, EL_KEY_1);
10468
10469       if (IS_KEY(element))
10470       {
10471         for (i = 0; i < MAX_PLAYERS; i++)
10472         {
10473           if (trigger_player_bits & (1 << i))
10474           {
10475             stored_player[i].key[KEY_NR(element)] = key_state;
10476
10477             DrawGameDoorValues();
10478           }
10479         }
10480       }
10481
10482       break;
10483     }
10484
10485     case CA_SET_PLAYER_SPEED:
10486     {
10487 #if 0
10488       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10489 #endif
10490
10491       for (i = 0; i < MAX_PLAYERS; i++)
10492       {
10493         if (trigger_player_bits & (1 << i))
10494         {
10495           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10496
10497           if (action_arg == CA_ARG_SPEED_FASTER &&
10498               stored_player[i].cannot_move)
10499           {
10500             action_arg_number = STEPSIZE_VERY_SLOW;
10501           }
10502           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10503                    action_arg == CA_ARG_SPEED_FASTER)
10504           {
10505             action_arg_number = 2;
10506             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10507                            CA_MODE_MULTIPLY);
10508           }
10509           else if (action_arg == CA_ARG_NUMBER_RESET)
10510           {
10511             action_arg_number = level.initial_player_stepsize[i];
10512           }
10513
10514           move_stepsize =
10515             getModifiedActionNumber(move_stepsize,
10516                                     action_mode,
10517                                     action_arg_number,
10518                                     action_arg_number_min,
10519                                     action_arg_number_max);
10520
10521           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10522         }
10523       }
10524
10525       break;
10526     }
10527
10528     case CA_SET_PLAYER_SHIELD:
10529     {
10530       for (i = 0; i < MAX_PLAYERS; i++)
10531       {
10532         if (trigger_player_bits & (1 << i))
10533         {
10534           if (action_arg == CA_ARG_SHIELD_OFF)
10535           {
10536             stored_player[i].shield_normal_time_left = 0;
10537             stored_player[i].shield_deadly_time_left = 0;
10538           }
10539           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10540           {
10541             stored_player[i].shield_normal_time_left = 999999;
10542           }
10543           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10544           {
10545             stored_player[i].shield_normal_time_left = 999999;
10546             stored_player[i].shield_deadly_time_left = 999999;
10547           }
10548         }
10549       }
10550
10551       break;
10552     }
10553
10554 #if USE_PLAYER_GRAVITY
10555     case CA_SET_PLAYER_GRAVITY:
10556     {
10557       for (i = 0; i < MAX_PLAYERS; i++)
10558       {
10559         if (trigger_player_bits & (1 << i))
10560         {
10561           stored_player[i].gravity =
10562             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10563              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10564              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10565              stored_player[i].gravity);
10566         }
10567       }
10568
10569       break;
10570     }
10571 #endif
10572
10573     case CA_SET_PLAYER_ARTWORK:
10574     {
10575       for (i = 0; i < MAX_PLAYERS; i++)
10576       {
10577         if (trigger_player_bits & (1 << i))
10578         {
10579           int artwork_element = action_arg_element;
10580
10581           if (action_arg == CA_ARG_ELEMENT_RESET)
10582             artwork_element =
10583               (level.use_artwork_element[i] ? level.artwork_element[i] :
10584                stored_player[i].element_nr);
10585
10586 #if USE_GFX_RESET_PLAYER_ARTWORK
10587           if (stored_player[i].artwork_element != artwork_element)
10588             stored_player[i].Frame = 0;
10589 #endif
10590
10591           stored_player[i].artwork_element = artwork_element;
10592
10593           SetPlayerWaiting(&stored_player[i], FALSE);
10594
10595           /* set number of special actions for bored and sleeping animation */
10596           stored_player[i].num_special_action_bored =
10597             get_num_special_action(artwork_element,
10598                                    ACTION_BORING_1, ACTION_BORING_LAST);
10599           stored_player[i].num_special_action_sleeping =
10600             get_num_special_action(artwork_element,
10601                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10602         }
10603       }
10604
10605       break;
10606     }
10607
10608     case CA_SET_PLAYER_INVENTORY:
10609     {
10610       for (i = 0; i < MAX_PLAYERS; i++)
10611       {
10612         struct PlayerInfo *player = &stored_player[i];
10613         int j, k;
10614
10615         if (trigger_player_bits & (1 << i))
10616         {
10617           int inventory_element = action_arg_element;
10618
10619           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10620               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10621               action_arg == CA_ARG_ELEMENT_ACTION)
10622           {
10623             int element = inventory_element;
10624             int collect_count = element_info[element].collect_count_initial;
10625
10626             if (!IS_CUSTOM_ELEMENT(element))
10627               collect_count = 1;
10628
10629             if (collect_count == 0)
10630               player->inventory_infinite_element = element;
10631             else
10632               for (k = 0; k < collect_count; k++)
10633                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10634                   player->inventory_element[player->inventory_size++] =
10635                     element;
10636           }
10637           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10638                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10639                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10640           {
10641             if (player->inventory_infinite_element != EL_UNDEFINED &&
10642                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10643                                      action_arg_element_raw))
10644               player->inventory_infinite_element = EL_UNDEFINED;
10645
10646             for (k = 0, j = 0; j < player->inventory_size; j++)
10647             {
10648               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10649                                         action_arg_element_raw))
10650                 player->inventory_element[k++] = player->inventory_element[j];
10651             }
10652
10653             player->inventory_size = k;
10654           }
10655           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10656           {
10657             if (player->inventory_size > 0)
10658             {
10659               for (j = 0; j < player->inventory_size - 1; j++)
10660                 player->inventory_element[j] = player->inventory_element[j + 1];
10661
10662               player->inventory_size--;
10663             }
10664           }
10665           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10666           {
10667             if (player->inventory_size > 0)
10668               player->inventory_size--;
10669           }
10670           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10671           {
10672             player->inventory_infinite_element = EL_UNDEFINED;
10673             player->inventory_size = 0;
10674           }
10675           else if (action_arg == CA_ARG_INVENTORY_RESET)
10676           {
10677             player->inventory_infinite_element = EL_UNDEFINED;
10678             player->inventory_size = 0;
10679
10680             if (level.use_initial_inventory[i])
10681             {
10682               for (j = 0; j < level.initial_inventory_size[i]; j++)
10683               {
10684                 int element = level.initial_inventory_content[i][j];
10685                 int collect_count = element_info[element].collect_count_initial;
10686
10687                 if (!IS_CUSTOM_ELEMENT(element))
10688                   collect_count = 1;
10689
10690                 if (collect_count == 0)
10691                   player->inventory_infinite_element = element;
10692                 else
10693                   for (k = 0; k < collect_count; k++)
10694                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10695                       player->inventory_element[player->inventory_size++] =
10696                         element;
10697               }
10698             }
10699           }
10700         }
10701       }
10702
10703       break;
10704     }
10705
10706     /* ---------- CE actions  ---------------------------------------------- */
10707
10708     case CA_SET_CE_VALUE:
10709     {
10710 #if USE_NEW_CUSTOM_VALUE
10711       int last_ce_value = CustomValue[x][y];
10712
10713       CustomValue[x][y] = action_arg_number_new;
10714
10715       if (CustomValue[x][y] != last_ce_value)
10716       {
10717         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10718         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10719
10720         if (CustomValue[x][y] == 0)
10721         {
10722           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10723           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10724         }
10725       }
10726 #endif
10727
10728       break;
10729     }
10730
10731     case CA_SET_CE_SCORE:
10732     {
10733 #if USE_NEW_CUSTOM_VALUE
10734       int last_ce_score = ei->collect_score;
10735
10736       ei->collect_score = action_arg_number_new;
10737
10738       if (ei->collect_score != last_ce_score)
10739       {
10740         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10741         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10742
10743         if (ei->collect_score == 0)
10744         {
10745           int xx, yy;
10746
10747           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10748           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10749
10750           /*
10751             This is a very special case that seems to be a mixture between
10752             CheckElementChange() and CheckTriggeredElementChange(): while
10753             the first one only affects single elements that are triggered
10754             directly, the second one affects multiple elements in the playfield
10755             that are triggered indirectly by another element. This is a third
10756             case: Changing the CE score always affects multiple identical CEs,
10757             so every affected CE must be checked, not only the single CE for
10758             which the CE score was changed in the first place (as every instance
10759             of that CE shares the same CE score, and therefore also can change)!
10760           */
10761           SCAN_PLAYFIELD(xx, yy)
10762           {
10763             if (Feld[xx][yy] == element)
10764               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10765                                  CE_SCORE_GETS_ZERO);
10766           }
10767         }
10768       }
10769 #endif
10770
10771       break;
10772     }
10773
10774     case CA_SET_CE_ARTWORK:
10775     {
10776       int artwork_element = action_arg_element;
10777       boolean reset_frame = FALSE;
10778       int xx, yy;
10779
10780       if (action_arg == CA_ARG_ELEMENT_RESET)
10781         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10782                            element);
10783
10784       if (ei->gfx_element != artwork_element)
10785         reset_frame = TRUE;
10786
10787       ei->gfx_element = artwork_element;
10788
10789       SCAN_PLAYFIELD(xx, yy)
10790       {
10791         if (Feld[xx][yy] == element)
10792         {
10793           if (reset_frame)
10794           {
10795             ResetGfxAnimation(xx, yy);
10796             ResetRandomAnimationValue(xx, yy);
10797           }
10798
10799           TEST_DrawLevelField(xx, yy);
10800         }
10801       }
10802
10803       break;
10804     }
10805
10806     /* ---------- engine actions  ------------------------------------------ */
10807
10808     case CA_SET_ENGINE_SCAN_MODE:
10809     {
10810       InitPlayfieldScanMode(action_arg);
10811
10812       break;
10813     }
10814
10815     default:
10816       break;
10817   }
10818 }
10819
10820 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10821 {
10822   int old_element = Feld[x][y];
10823   int new_element = GetElementFromGroupElement(element);
10824   int previous_move_direction = MovDir[x][y];
10825 #if USE_NEW_CUSTOM_VALUE
10826   int last_ce_value = CustomValue[x][y];
10827 #endif
10828   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10829   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10830   boolean add_player_onto_element = (new_element_is_player &&
10831 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10832                                      /* this breaks SnakeBite when a snake is
10833                                         halfway through a door that closes */
10834                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10835                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10836 #endif
10837                                      IS_WALKABLE(old_element));
10838
10839 #if 0
10840   /* check if element under the player changes from accessible to unaccessible
10841      (needed for special case of dropping element which then changes) */
10842   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10843       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10844   {
10845     Bang(x, y);
10846
10847     return;
10848   }
10849 #endif
10850
10851   if (!add_player_onto_element)
10852   {
10853     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10854       RemoveMovingField(x, y);
10855     else
10856       RemoveField(x, y);
10857
10858     Feld[x][y] = new_element;
10859
10860 #if !USE_GFX_RESET_GFX_ANIMATION
10861     ResetGfxAnimation(x, y);
10862     ResetRandomAnimationValue(x, y);
10863 #endif
10864
10865     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10866       MovDir[x][y] = previous_move_direction;
10867
10868 #if USE_NEW_CUSTOM_VALUE
10869     if (element_info[new_element].use_last_ce_value)
10870       CustomValue[x][y] = last_ce_value;
10871 #endif
10872
10873     InitField_WithBug1(x, y, FALSE);
10874
10875     new_element = Feld[x][y];   /* element may have changed */
10876
10877 #if USE_GFX_RESET_GFX_ANIMATION
10878     ResetGfxAnimation(x, y);
10879     ResetRandomAnimationValue(x, y);
10880 #endif
10881
10882     TEST_DrawLevelField(x, y);
10883
10884     if (GFX_CRUMBLED(new_element))
10885       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10886   }
10887
10888 #if 1
10889   /* check if element under the player changes from accessible to unaccessible
10890      (needed for special case of dropping element which then changes) */
10891   /* (must be checked after creating new element for walkable group elements) */
10892 #if USE_FIX_KILLED_BY_NON_WALKABLE
10893   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10894       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10895   {
10896     Bang(x, y);
10897
10898     return;
10899   }
10900 #else
10901   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10902       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10903   {
10904     Bang(x, y);
10905
10906     return;
10907   }
10908 #endif
10909 #endif
10910
10911   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10912   if (new_element_is_player)
10913     RelocatePlayer(x, y, new_element);
10914
10915   if (is_change)
10916     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10917
10918   TestIfBadThingTouchesPlayer(x, y);
10919   TestIfPlayerTouchesCustomElement(x, y);
10920   TestIfElementTouchesCustomElement(x, y);
10921 }
10922
10923 static void CreateField(int x, int y, int element)
10924 {
10925   CreateFieldExt(x, y, element, FALSE);
10926 }
10927
10928 static void CreateElementFromChange(int x, int y, int element)
10929 {
10930   element = GET_VALID_RUNTIME_ELEMENT(element);
10931
10932 #if USE_STOP_CHANGED_ELEMENTS
10933   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10934   {
10935     int old_element = Feld[x][y];
10936
10937     /* prevent changed element from moving in same engine frame
10938        unless both old and new element can either fall or move */
10939     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10940         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10941       Stop[x][y] = TRUE;
10942   }
10943 #endif
10944
10945   CreateFieldExt(x, y, element, TRUE);
10946 }
10947
10948 static boolean ChangeElement(int x, int y, int element, int page)
10949 {
10950   struct ElementInfo *ei = &element_info[element];
10951   struct ElementChangeInfo *change = &ei->change_page[page];
10952   int ce_value = CustomValue[x][y];
10953   int ce_score = ei->collect_score;
10954   int target_element;
10955   int old_element = Feld[x][y];
10956
10957   /* always use default change event to prevent running into a loop */
10958   if (ChangeEvent[x][y] == -1)
10959     ChangeEvent[x][y] = CE_DELAY;
10960
10961   if (ChangeEvent[x][y] == CE_DELAY)
10962   {
10963     /* reset actual trigger element, trigger player and action element */
10964     change->actual_trigger_element = EL_EMPTY;
10965     change->actual_trigger_player = EL_EMPTY;
10966     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10967     change->actual_trigger_side = CH_SIDE_NONE;
10968     change->actual_trigger_ce_value = 0;
10969     change->actual_trigger_ce_score = 0;
10970   }
10971
10972   /* do not change elements more than a specified maximum number of changes */
10973   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10974     return FALSE;
10975
10976   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10977
10978   if (change->explode)
10979   {
10980     Bang(x, y);
10981
10982     return TRUE;
10983   }
10984
10985   if (change->use_target_content)
10986   {
10987     boolean complete_replace = TRUE;
10988     boolean can_replace[3][3];
10989     int xx, yy;
10990
10991     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10992     {
10993       boolean is_empty;
10994       boolean is_walkable;
10995       boolean is_diggable;
10996       boolean is_collectible;
10997       boolean is_removable;
10998       boolean is_destructible;
10999       int ex = x + xx - 1;
11000       int ey = y + yy - 1;
11001       int content_element = change->target_content.e[xx][yy];
11002       int e;
11003
11004       can_replace[xx][yy] = TRUE;
11005
11006       if (ex == x && ey == y)   /* do not check changing element itself */
11007         continue;
11008
11009       if (content_element == EL_EMPTY_SPACE)
11010       {
11011         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11012
11013         continue;
11014       }
11015
11016       if (!IN_LEV_FIELD(ex, ey))
11017       {
11018         can_replace[xx][yy] = FALSE;
11019         complete_replace = FALSE;
11020
11021         continue;
11022       }
11023
11024       e = Feld[ex][ey];
11025
11026       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11027         e = MovingOrBlocked2Element(ex, ey);
11028
11029       is_empty = (IS_FREE(ex, ey) ||
11030                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11031
11032       is_walkable     = (is_empty || IS_WALKABLE(e));
11033       is_diggable     = (is_empty || IS_DIGGABLE(e));
11034       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11035       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11036       is_removable    = (is_diggable || is_collectible);
11037
11038       can_replace[xx][yy] =
11039         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11040           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11041           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11042           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11043           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11044           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11045          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11046
11047       if (!can_replace[xx][yy])
11048         complete_replace = FALSE;
11049     }
11050
11051     if (!change->only_if_complete || complete_replace)
11052     {
11053       boolean something_has_changed = FALSE;
11054
11055       if (change->only_if_complete && change->use_random_replace &&
11056           RND(100) < change->random_percentage)
11057         return FALSE;
11058
11059       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11060       {
11061         int ex = x + xx - 1;
11062         int ey = y + yy - 1;
11063         int content_element;
11064
11065         if (can_replace[xx][yy] && (!change->use_random_replace ||
11066                                     RND(100) < change->random_percentage))
11067         {
11068           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11069             RemoveMovingField(ex, ey);
11070
11071           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11072
11073           content_element = change->target_content.e[xx][yy];
11074           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11075                                               ce_value, ce_score);
11076
11077           CreateElementFromChange(ex, ey, target_element);
11078
11079           something_has_changed = TRUE;
11080
11081           /* for symmetry reasons, freeze newly created border elements */
11082           if (ex != x || ey != y)
11083             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11084         }
11085       }
11086
11087       if (something_has_changed)
11088       {
11089         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11090         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11091       }
11092     }
11093   }
11094   else
11095   {
11096     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11097                                         ce_value, ce_score);
11098
11099     if (element == EL_DIAGONAL_GROWING ||
11100         element == EL_DIAGONAL_SHRINKING)
11101     {
11102       target_element = Store[x][y];
11103
11104       Store[x][y] = EL_EMPTY;
11105     }
11106
11107     CreateElementFromChange(x, y, target_element);
11108
11109     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11110     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11111   }
11112
11113   /* this uses direct change before indirect change */
11114   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11115
11116   return TRUE;
11117 }
11118
11119 #if USE_NEW_DELAYED_ACTION
11120
11121 static void HandleElementChange(int x, int y, int page)
11122 {
11123   int element = MovingOrBlocked2Element(x, y);
11124   struct ElementInfo *ei = &element_info[element];
11125   struct ElementChangeInfo *change = &ei->change_page[page];
11126
11127 #ifdef DEBUG
11128   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11129       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11130   {
11131     printf("\n\n");
11132     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11133            x, y, element, element_info[element].token_name);
11134     printf("HandleElementChange(): This should never happen!\n");
11135     printf("\n\n");
11136   }
11137 #endif
11138
11139   /* this can happen with classic bombs on walkable, changing elements */
11140   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11141   {
11142 #if 0
11143     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11144       ChangeDelay[x][y] = 0;
11145 #endif
11146
11147     return;
11148   }
11149
11150   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11151   {
11152     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11153
11154     if (change->can_change)
11155     {
11156 #if 1
11157       /* !!! not clear why graphic animation should be reset at all here !!! */
11158       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11159 #if USE_GFX_RESET_WHEN_NOT_MOVING
11160       /* when a custom element is about to change (for example by change delay),
11161          do not reset graphic animation when the custom element is moving */
11162       if (!IS_MOVING(x, y))
11163 #endif
11164       {
11165         ResetGfxAnimation(x, y);
11166         ResetRandomAnimationValue(x, y);
11167       }
11168 #endif
11169
11170       if (change->pre_change_function)
11171         change->pre_change_function(x, y);
11172     }
11173   }
11174
11175   ChangeDelay[x][y]--;
11176
11177   if (ChangeDelay[x][y] != 0)           /* continue element change */
11178   {
11179     if (change->can_change)
11180     {
11181       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11182
11183       if (IS_ANIMATED(graphic))
11184         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11185
11186       if (change->change_function)
11187         change->change_function(x, y);
11188     }
11189   }
11190   else                                  /* finish element change */
11191   {
11192     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11193     {
11194       page = ChangePage[x][y];
11195       ChangePage[x][y] = -1;
11196
11197       change = &ei->change_page[page];
11198     }
11199
11200     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11201     {
11202       ChangeDelay[x][y] = 1;            /* try change after next move step */
11203       ChangePage[x][y] = page;          /* remember page to use for change */
11204
11205       return;
11206     }
11207
11208     if (change->can_change)
11209     {
11210       if (ChangeElement(x, y, element, page))
11211       {
11212         if (change->post_change_function)
11213           change->post_change_function(x, y);
11214       }
11215     }
11216
11217     if (change->has_action)
11218       ExecuteCustomElementAction(x, y, element, page);
11219   }
11220 }
11221
11222 #else
11223
11224 static void HandleElementChange(int x, int y, int page)
11225 {
11226   int element = MovingOrBlocked2Element(x, y);
11227   struct ElementInfo *ei = &element_info[element];
11228   struct ElementChangeInfo *change = &ei->change_page[page];
11229
11230 #ifdef DEBUG
11231   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11232   {
11233     printf("\n\n");
11234     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11235            x, y, element, element_info[element].token_name);
11236     printf("HandleElementChange(): This should never happen!\n");
11237     printf("\n\n");
11238   }
11239 #endif
11240
11241   /* this can happen with classic bombs on walkable, changing elements */
11242   if (!CAN_CHANGE(element))
11243   {
11244 #if 0
11245     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11246       ChangeDelay[x][y] = 0;
11247 #endif
11248
11249     return;
11250   }
11251
11252   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11253   {
11254     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11255
11256     ResetGfxAnimation(x, y);
11257     ResetRandomAnimationValue(x, y);
11258
11259     if (change->pre_change_function)
11260       change->pre_change_function(x, y);
11261   }
11262
11263   ChangeDelay[x][y]--;
11264
11265   if (ChangeDelay[x][y] != 0)           /* continue element change */
11266   {
11267     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11268
11269     if (IS_ANIMATED(graphic))
11270       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11271
11272     if (change->change_function)
11273       change->change_function(x, y);
11274   }
11275   else                                  /* finish element change */
11276   {
11277     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11278     {
11279       page = ChangePage[x][y];
11280       ChangePage[x][y] = -1;
11281
11282       change = &ei->change_page[page];
11283     }
11284
11285     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11286     {
11287       ChangeDelay[x][y] = 1;            /* try change after next move step */
11288       ChangePage[x][y] = page;          /* remember page to use for change */
11289
11290       return;
11291     }
11292
11293     if (ChangeElement(x, y, element, page))
11294     {
11295       if (change->post_change_function)
11296         change->post_change_function(x, y);
11297     }
11298   }
11299 }
11300
11301 #endif
11302
11303 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11304                                               int trigger_element,
11305                                               int trigger_event,
11306                                               int trigger_player,
11307                                               int trigger_side,
11308                                               int trigger_page)
11309 {
11310   boolean change_done_any = FALSE;
11311   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11312   int i;
11313
11314   if (!(trigger_events[trigger_element][trigger_event]))
11315     return FALSE;
11316
11317 #if 0
11318   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11319          trigger_event, recursion_loop_depth, recursion_loop_detected,
11320          recursion_loop_element, EL_NAME(recursion_loop_element));
11321 #endif
11322
11323   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11324
11325   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11326   {
11327     int element = EL_CUSTOM_START + i;
11328     boolean change_done = FALSE;
11329     int p;
11330
11331     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11332         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11333       continue;
11334
11335     for (p = 0; p < element_info[element].num_change_pages; p++)
11336     {
11337       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11338
11339       if (change->can_change_or_has_action &&
11340           change->has_event[trigger_event] &&
11341           change->trigger_side & trigger_side &&
11342           change->trigger_player & trigger_player &&
11343           change->trigger_page & trigger_page_bits &&
11344           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11345       {
11346         change->actual_trigger_element = trigger_element;
11347         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11348         change->actual_trigger_player_bits = trigger_player;
11349         change->actual_trigger_side = trigger_side;
11350         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11351         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11352
11353 #if 0
11354         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11355                element, EL_NAME(element), p);
11356 #endif
11357
11358         if ((change->can_change && !change_done) || change->has_action)
11359         {
11360           int x, y;
11361
11362           SCAN_PLAYFIELD(x, y)
11363           {
11364             if (Feld[x][y] == element)
11365             {
11366               if (change->can_change && !change_done)
11367               {
11368 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11369                 /* if element already changed in this frame, not only prevent
11370                    another element change (checked in ChangeElement()), but
11371                    also prevent additional element actions for this element */
11372
11373                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11374                     !level.use_action_after_change_bug)
11375                   continue;
11376 #endif
11377
11378 #if 0
11379                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11380                        element, EL_NAME(element), p);
11381 #endif
11382
11383                 ChangeDelay[x][y] = 1;
11384                 ChangeEvent[x][y] = trigger_event;
11385
11386                 HandleElementChange(x, y, p);
11387               }
11388 #if USE_NEW_DELAYED_ACTION
11389               else if (change->has_action)
11390               {
11391 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11392                 /* if element already changed in this frame, not only prevent
11393                    another element change (checked in ChangeElement()), but
11394                    also prevent additional element actions for this element */
11395
11396                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11397                     !level.use_action_after_change_bug)
11398                   continue;
11399 #endif
11400
11401
11402 #if 0
11403                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11404                        element, EL_NAME(element), p);
11405 #endif
11406
11407                 ExecuteCustomElementAction(x, y, element, p);
11408                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11409               }
11410 #else
11411               if (change->has_action)
11412               {
11413                 ExecuteCustomElementAction(x, y, element, p);
11414                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11415               }
11416 #endif
11417             }
11418           }
11419
11420           if (change->can_change)
11421           {
11422             change_done = TRUE;
11423             change_done_any = TRUE;
11424
11425 #if 0
11426             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11427                    element, EL_NAME(element), p);
11428 #endif
11429
11430           }
11431         }
11432       }
11433     }
11434   }
11435
11436   RECURSION_LOOP_DETECTION_END();
11437
11438   return change_done_any;
11439 }
11440
11441 static boolean CheckElementChangeExt(int x, int y,
11442                                      int element,
11443                                      int trigger_element,
11444                                      int trigger_event,
11445                                      int trigger_player,
11446                                      int trigger_side)
11447 {
11448   boolean change_done = FALSE;
11449   int p;
11450
11451   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11452       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11453     return FALSE;
11454
11455   if (Feld[x][y] == EL_BLOCKED)
11456   {
11457     Blocked2Moving(x, y, &x, &y);
11458     element = Feld[x][y];
11459   }
11460
11461 #if 0
11462   /* check if element has already changed */
11463   if (Feld[x][y] != element)
11464     return FALSE;
11465 #else
11466   /* check if element has already changed or is about to change after moving */
11467   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11468        Feld[x][y] != element) ||
11469
11470       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11471        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11472         ChangePage[x][y] != -1)))
11473     return FALSE;
11474 #endif
11475
11476 #if 0
11477   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11478          trigger_event, recursion_loop_depth, recursion_loop_detected,
11479          recursion_loop_element, EL_NAME(recursion_loop_element));
11480 #endif
11481
11482   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11483
11484 #if 0
11485   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11486 #endif
11487
11488   for (p = 0; p < element_info[element].num_change_pages; p++)
11489   {
11490     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11491
11492     /* check trigger element for all events where the element that is checked
11493        for changing interacts with a directly adjacent element -- this is
11494        different to element changes that affect other elements to change on the
11495        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11496     boolean check_trigger_element =
11497       (trigger_event == CE_TOUCHING_X ||
11498        trigger_event == CE_HITTING_X ||
11499        trigger_event == CE_HIT_BY_X ||
11500 #if 1
11501        /* this one was forgotten until 3.2.3 */
11502        trigger_event == CE_DIGGING_X);
11503 #endif
11504
11505     if (change->can_change_or_has_action &&
11506         change->has_event[trigger_event] &&
11507         change->trigger_side & trigger_side &&
11508         change->trigger_player & trigger_player &&
11509         (!check_trigger_element ||
11510          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11511     {
11512       change->actual_trigger_element = trigger_element;
11513       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11514       change->actual_trigger_player_bits = trigger_player;
11515       change->actual_trigger_side = trigger_side;
11516       change->actual_trigger_ce_value = CustomValue[x][y];
11517       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11518
11519       /* special case: trigger element not at (x,y) position for some events */
11520       if (check_trigger_element)
11521       {
11522         static struct
11523         {
11524           int dx, dy;
11525         } move_xy[] =
11526           {
11527             {  0,  0 },
11528             { -1,  0 },
11529             { +1,  0 },
11530             {  0,  0 },
11531             {  0, -1 },
11532             {  0,  0 }, { 0, 0 }, { 0, 0 },
11533             {  0, +1 }
11534           };
11535
11536         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11537         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11538
11539         change->actual_trigger_ce_value = CustomValue[xx][yy];
11540         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11541       }
11542
11543       if (change->can_change && !change_done)
11544       {
11545         ChangeDelay[x][y] = 1;
11546         ChangeEvent[x][y] = trigger_event;
11547
11548         HandleElementChange(x, y, p);
11549
11550         change_done = TRUE;
11551       }
11552 #if USE_NEW_DELAYED_ACTION
11553       else if (change->has_action)
11554       {
11555         ExecuteCustomElementAction(x, y, element, p);
11556         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11557       }
11558 #else
11559       if (change->has_action)
11560       {
11561         ExecuteCustomElementAction(x, y, element, p);
11562         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11563       }
11564 #endif
11565     }
11566   }
11567
11568   RECURSION_LOOP_DETECTION_END();
11569
11570   return change_done;
11571 }
11572
11573 static void PlayPlayerSound(struct PlayerInfo *player)
11574 {
11575   int jx = player->jx, jy = player->jy;
11576   int sound_element = player->artwork_element;
11577   int last_action = player->last_action_waiting;
11578   int action = player->action_waiting;
11579
11580   if (player->is_waiting)
11581   {
11582     if (action != last_action)
11583       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11584     else
11585       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11586   }
11587   else
11588   {
11589     if (action != last_action)
11590       StopSound(element_info[sound_element].sound[last_action]);
11591
11592     if (last_action == ACTION_SLEEPING)
11593       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11594   }
11595 }
11596
11597 static void PlayAllPlayersSound()
11598 {
11599   int i;
11600
11601   for (i = 0; i < MAX_PLAYERS; i++)
11602     if (stored_player[i].active)
11603       PlayPlayerSound(&stored_player[i]);
11604 }
11605
11606 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11607 {
11608   boolean last_waiting = player->is_waiting;
11609   int move_dir = player->MovDir;
11610
11611   player->dir_waiting = move_dir;
11612   player->last_action_waiting = player->action_waiting;
11613
11614   if (is_waiting)
11615   {
11616     if (!last_waiting)          /* not waiting -> waiting */
11617     {
11618       player->is_waiting = TRUE;
11619
11620       player->frame_counter_bored =
11621         FrameCounter +
11622         game.player_boring_delay_fixed +
11623         GetSimpleRandom(game.player_boring_delay_random);
11624       player->frame_counter_sleeping =
11625         FrameCounter +
11626         game.player_sleeping_delay_fixed +
11627         GetSimpleRandom(game.player_sleeping_delay_random);
11628
11629       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11630     }
11631
11632     if (game.player_sleeping_delay_fixed +
11633         game.player_sleeping_delay_random > 0 &&
11634         player->anim_delay_counter == 0 &&
11635         player->post_delay_counter == 0 &&
11636         FrameCounter >= player->frame_counter_sleeping)
11637       player->is_sleeping = TRUE;
11638     else if (game.player_boring_delay_fixed +
11639              game.player_boring_delay_random > 0 &&
11640              FrameCounter >= player->frame_counter_bored)
11641       player->is_bored = TRUE;
11642
11643     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11644                               player->is_bored ? ACTION_BORING :
11645                               ACTION_WAITING);
11646
11647     if (player->is_sleeping && player->use_murphy)
11648     {
11649       /* special case for sleeping Murphy when leaning against non-free tile */
11650
11651       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11652           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11653            !IS_MOVING(player->jx - 1, player->jy)))
11654         move_dir = MV_LEFT;
11655       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11656                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11657                 !IS_MOVING(player->jx + 1, player->jy)))
11658         move_dir = MV_RIGHT;
11659       else
11660         player->is_sleeping = FALSE;
11661
11662       player->dir_waiting = move_dir;
11663     }
11664
11665     if (player->is_sleeping)
11666     {
11667       if (player->num_special_action_sleeping > 0)
11668       {
11669         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11670         {
11671           int last_special_action = player->special_action_sleeping;
11672           int num_special_action = player->num_special_action_sleeping;
11673           int special_action =
11674             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11675              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11676              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11677              last_special_action + 1 : ACTION_SLEEPING);
11678           int special_graphic =
11679             el_act_dir2img(player->artwork_element, special_action, move_dir);
11680
11681           player->anim_delay_counter =
11682             graphic_info[special_graphic].anim_delay_fixed +
11683             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11684           player->post_delay_counter =
11685             graphic_info[special_graphic].post_delay_fixed +
11686             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11687
11688           player->special_action_sleeping = special_action;
11689         }
11690
11691         if (player->anim_delay_counter > 0)
11692         {
11693           player->action_waiting = player->special_action_sleeping;
11694           player->anim_delay_counter--;
11695         }
11696         else if (player->post_delay_counter > 0)
11697         {
11698           player->post_delay_counter--;
11699         }
11700       }
11701     }
11702     else if (player->is_bored)
11703     {
11704       if (player->num_special_action_bored > 0)
11705       {
11706         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11707         {
11708           int special_action =
11709             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11710           int special_graphic =
11711             el_act_dir2img(player->artwork_element, special_action, move_dir);
11712
11713           player->anim_delay_counter =
11714             graphic_info[special_graphic].anim_delay_fixed +
11715             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11716           player->post_delay_counter =
11717             graphic_info[special_graphic].post_delay_fixed +
11718             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11719
11720           player->special_action_bored = special_action;
11721         }
11722
11723         if (player->anim_delay_counter > 0)
11724         {
11725           player->action_waiting = player->special_action_bored;
11726           player->anim_delay_counter--;
11727         }
11728         else if (player->post_delay_counter > 0)
11729         {
11730           player->post_delay_counter--;
11731         }
11732       }
11733     }
11734   }
11735   else if (last_waiting)        /* waiting -> not waiting */
11736   {
11737     player->is_waiting = FALSE;
11738     player->is_bored = FALSE;
11739     player->is_sleeping = FALSE;
11740
11741     player->frame_counter_bored = -1;
11742     player->frame_counter_sleeping = -1;
11743
11744     player->anim_delay_counter = 0;
11745     player->post_delay_counter = 0;
11746
11747     player->dir_waiting = player->MovDir;
11748     player->action_waiting = ACTION_DEFAULT;
11749
11750     player->special_action_bored = ACTION_DEFAULT;
11751     player->special_action_sleeping = ACTION_DEFAULT;
11752   }
11753 }
11754
11755 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11756 {
11757   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11758   int left      = player_action & JOY_LEFT;
11759   int right     = player_action & JOY_RIGHT;
11760   int up        = player_action & JOY_UP;
11761   int down      = player_action & JOY_DOWN;
11762   int button1   = player_action & JOY_BUTTON_1;
11763   int button2   = player_action & JOY_BUTTON_2;
11764   int dx        = (left ? -1 : right ? 1 : 0);
11765   int dy        = (up   ? -1 : down  ? 1 : 0);
11766
11767   if (!player->active || tape.pausing)
11768     return 0;
11769
11770   if (player_action)
11771   {
11772     if (button1)
11773       snapped = SnapField(player, dx, dy);
11774     else
11775     {
11776       if (button2)
11777         dropped = DropElement(player);
11778
11779       moved = MovePlayer(player, dx, dy);
11780     }
11781
11782     if (tape.single_step && tape.recording && !tape.pausing)
11783     {
11784       if (button1 || (dropped && !moved))
11785       {
11786         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11787         SnapField(player, 0, 0);                /* stop snapping */
11788       }
11789     }
11790
11791     SetPlayerWaiting(player, FALSE);
11792
11793     return player_action;
11794   }
11795   else
11796   {
11797     /* no actions for this player (no input at player's configured device) */
11798
11799     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11800     SnapField(player, 0, 0);
11801     CheckGravityMovementWhenNotMoving(player);
11802
11803     if (player->MovPos == 0)
11804       SetPlayerWaiting(player, TRUE);
11805
11806     if (player->MovPos == 0)    /* needed for tape.playing */
11807       player->is_moving = FALSE;
11808
11809     player->is_dropping = FALSE;
11810     player->is_dropping_pressed = FALSE;
11811     player->drop_pressed_delay = 0;
11812
11813     return 0;
11814   }
11815 }
11816
11817 static void CheckLevelTime()
11818 {
11819   int i;
11820
11821   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11822   {
11823     if (level.native_em_level->lev->home == 0)  /* all players at home */
11824     {
11825       PlayerWins(local_player);
11826
11827       AllPlayersGone = TRUE;
11828
11829       level.native_em_level->lev->home = -1;
11830     }
11831
11832     if (level.native_em_level->ply[0]->alive == 0 &&
11833         level.native_em_level->ply[1]->alive == 0 &&
11834         level.native_em_level->ply[2]->alive == 0 &&
11835         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11836       AllPlayersGone = TRUE;
11837   }
11838
11839   if (TimeFrames >= FRAMES_PER_SECOND)
11840   {
11841     TimeFrames = 0;
11842     TapeTime++;
11843
11844     for (i = 0; i < MAX_PLAYERS; i++)
11845     {
11846       struct PlayerInfo *player = &stored_player[i];
11847
11848       if (SHIELD_ON(player))
11849       {
11850         player->shield_normal_time_left--;
11851
11852         if (player->shield_deadly_time_left > 0)
11853           player->shield_deadly_time_left--;
11854       }
11855     }
11856
11857     if (!local_player->LevelSolved && !level.use_step_counter)
11858     {
11859       TimePlayed++;
11860
11861       if (TimeLeft > 0)
11862       {
11863         TimeLeft--;
11864
11865         if (TimeLeft <= 10 && setup.time_limit)
11866           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11867
11868 #if 1
11869         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11870
11871         DisplayGameControlValues();
11872 #else
11873         DrawGameValue_Time(TimeLeft);
11874 #endif
11875
11876         if (!TimeLeft && setup.time_limit)
11877         {
11878           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11879             level.native_em_level->lev->killed_out_of_time = TRUE;
11880           else
11881             for (i = 0; i < MAX_PLAYERS; i++)
11882               KillPlayer(&stored_player[i]);
11883         }
11884       }
11885 #if 1
11886       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11887       {
11888         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11889
11890         DisplayGameControlValues();
11891       }
11892 #else
11893       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11894         DrawGameValue_Time(TimePlayed);
11895 #endif
11896
11897       level.native_em_level->lev->time =
11898         (level.time == 0 ? TimePlayed : TimeLeft);
11899     }
11900
11901     if (tape.recording || tape.playing)
11902       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11903   }
11904
11905 #if 1
11906   UpdateAndDisplayGameControlValues();
11907 #else
11908   UpdateGameDoorValues();
11909   DrawGameDoorValues();
11910 #endif
11911 }
11912
11913 void AdvanceFrameAndPlayerCounters(int player_nr)
11914 {
11915   int i;
11916
11917   /* advance frame counters (global frame counter and time frame counter) */
11918   FrameCounter++;
11919   TimeFrames++;
11920
11921   /* advance player counters (counters for move delay, move animation etc.) */
11922   for (i = 0; i < MAX_PLAYERS; i++)
11923   {
11924     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11925     int move_delay_value = stored_player[i].move_delay_value;
11926     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11927
11928     if (!advance_player_counters)       /* not all players may be affected */
11929       continue;
11930
11931 #if USE_NEW_PLAYER_ANIM
11932     if (move_frames == 0)       /* less than one move per game frame */
11933     {
11934       int stepsize = TILEX / move_delay_value;
11935       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11936       int count = (stored_player[i].is_moving ?
11937                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11938
11939       if (count % delay == 0)
11940         move_frames = 1;
11941     }
11942 #endif
11943
11944     stored_player[i].Frame += move_frames;
11945
11946     if (stored_player[i].MovPos != 0)
11947       stored_player[i].StepFrame += move_frames;
11948
11949     if (stored_player[i].move_delay > 0)
11950       stored_player[i].move_delay--;
11951
11952     /* due to bugs in previous versions, counter must count up, not down */
11953     if (stored_player[i].push_delay != -1)
11954       stored_player[i].push_delay++;
11955
11956     if (stored_player[i].drop_delay > 0)
11957       stored_player[i].drop_delay--;
11958
11959     if (stored_player[i].is_dropping_pressed)
11960       stored_player[i].drop_pressed_delay++;
11961   }
11962 }
11963
11964 void StartGameActions(boolean init_network_game, boolean record_tape,
11965                       long random_seed)
11966 {
11967   unsigned long new_random_seed = InitRND(random_seed);
11968
11969   if (record_tape)
11970     TapeStartRecording(new_random_seed);
11971
11972 #if defined(NETWORK_AVALIABLE)
11973   if (init_network_game)
11974   {
11975     SendToServer_StartPlaying();
11976
11977     return;
11978   }
11979 #endif
11980
11981   InitGame();
11982 }
11983
11984 void GameActions()
11985 {
11986   static unsigned long game_frame_delay = 0;
11987   unsigned long game_frame_delay_value;
11988   byte *recorded_player_action;
11989   byte summarized_player_action = 0;
11990   byte tape_action[MAX_PLAYERS];
11991   int i;
11992
11993   /* detect endless loops, caused by custom element programming */
11994   if (recursion_loop_detected && recursion_loop_depth == 0)
11995   {
11996     char *message = getStringCat3("Internal Error ! Element ",
11997                                   EL_NAME(recursion_loop_element),
11998                                   " caused endless loop ! Quit the game ?");
11999
12000     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12001           EL_NAME(recursion_loop_element));
12002
12003     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12004
12005     recursion_loop_detected = FALSE;    /* if game should be continued */
12006
12007     free(message);
12008
12009     return;
12010   }
12011
12012   if (game.restart_level)
12013     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
12014
12015   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12016   {
12017     if (level.native_em_level->lev->home == 0)  /* all players at home */
12018     {
12019       PlayerWins(local_player);
12020
12021       AllPlayersGone = TRUE;
12022
12023       level.native_em_level->lev->home = -1;
12024     }
12025
12026     if (level.native_em_level->ply[0]->alive == 0 &&
12027         level.native_em_level->ply[1]->alive == 0 &&
12028         level.native_em_level->ply[2]->alive == 0 &&
12029         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12030       AllPlayersGone = TRUE;
12031   }
12032
12033   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12034     GameWon();
12035
12036   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12037     TapeStop();
12038
12039   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12040     return;
12041
12042   game_frame_delay_value =
12043     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12044
12045   if (tape.playing && tape.warp_forward && !tape.pausing)
12046     game_frame_delay_value = 0;
12047
12048   /* ---------- main game synchronization point ---------- */
12049
12050   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12051
12052   if (network_playing && !network_player_action_received)
12053   {
12054     /* try to get network player actions in time */
12055
12056 #if defined(NETWORK_AVALIABLE)
12057     /* last chance to get network player actions without main loop delay */
12058     HandleNetworking();
12059 #endif
12060
12061     /* game was quit by network peer */
12062     if (game_status != GAME_MODE_PLAYING)
12063       return;
12064
12065     if (!network_player_action_received)
12066       return;           /* failed to get network player actions in time */
12067
12068     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12069   }
12070
12071   if (tape.pausing)
12072     return;
12073
12074   /* at this point we know that we really continue executing the game */
12075
12076   network_player_action_received = FALSE;
12077
12078   /* when playing tape, read previously recorded player input from tape data */
12079   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12080
12081 #if 1
12082   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12083   if (tape.pausing)
12084     return;
12085 #endif
12086
12087   if (tape.set_centered_player)
12088   {
12089     game.centered_player_nr_next = tape.centered_player_nr_next;
12090     game.set_centered_player = TRUE;
12091   }
12092
12093   for (i = 0; i < MAX_PLAYERS; i++)
12094   {
12095     summarized_player_action |= stored_player[i].action;
12096
12097     if (!network_playing)
12098       stored_player[i].effective_action = stored_player[i].action;
12099   }
12100
12101 #if defined(NETWORK_AVALIABLE)
12102   if (network_playing)
12103     SendToServer_MovePlayer(summarized_player_action);
12104 #endif
12105
12106   if (!options.network && !setup.team_mode)
12107     local_player->effective_action = summarized_player_action;
12108
12109   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12110   {
12111     for (i = 0; i < MAX_PLAYERS; i++)
12112       stored_player[i].effective_action =
12113         (i == game.centered_player_nr ? summarized_player_action : 0);
12114   }
12115
12116   if (recorded_player_action != NULL)
12117     for (i = 0; i < MAX_PLAYERS; i++)
12118       stored_player[i].effective_action = recorded_player_action[i];
12119
12120   for (i = 0; i < MAX_PLAYERS; i++)
12121   {
12122     tape_action[i] = stored_player[i].effective_action;
12123
12124     /* (this can only happen in the R'n'D game engine) */
12125     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12126       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12127   }
12128
12129   /* only record actions from input devices, but not programmed actions */
12130   if (tape.recording)
12131     TapeRecordAction(tape_action);
12132
12133   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12134   {
12135     GameActions_EM_Main();
12136   }
12137   else
12138   {
12139     GameActions_RND();
12140   }
12141 }
12142
12143 void GameActions_EM_Main()
12144 {
12145   byte effective_action[MAX_PLAYERS];
12146   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12147   int i;
12148
12149   for (i = 0; i < MAX_PLAYERS; i++)
12150     effective_action[i] = stored_player[i].effective_action;
12151
12152   GameActions_EM(effective_action, warp_mode);
12153
12154   CheckLevelTime();
12155
12156   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12157 }
12158
12159 void GameActions_RND()
12160 {
12161   int magic_wall_x = 0, magic_wall_y = 0;
12162   int i, x, y, element, graphic;
12163
12164   InitPlayfieldScanModeVars();
12165
12166 #if USE_ONE_MORE_CHANGE_PER_FRAME
12167   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12168   {
12169     SCAN_PLAYFIELD(x, y)
12170     {
12171       ChangeCount[x][y] = 0;
12172       ChangeEvent[x][y] = -1;
12173     }
12174   }
12175 #endif
12176
12177   if (game.set_centered_player)
12178   {
12179     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12180
12181     /* switching to "all players" only possible if all players fit to screen */
12182     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12183     {
12184       game.centered_player_nr_next = game.centered_player_nr;
12185       game.set_centered_player = FALSE;
12186     }
12187
12188     /* do not switch focus to non-existing (or non-active) player */
12189     if (game.centered_player_nr_next >= 0 &&
12190         !stored_player[game.centered_player_nr_next].active)
12191     {
12192       game.centered_player_nr_next = game.centered_player_nr;
12193       game.set_centered_player = FALSE;
12194     }
12195   }
12196
12197   if (game.set_centered_player &&
12198       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12199   {
12200     int sx, sy;
12201
12202     if (game.centered_player_nr_next == -1)
12203     {
12204       setScreenCenteredToAllPlayers(&sx, &sy);
12205     }
12206     else
12207     {
12208       sx = stored_player[game.centered_player_nr_next].jx;
12209       sy = stored_player[game.centered_player_nr_next].jy;
12210     }
12211
12212     game.centered_player_nr = game.centered_player_nr_next;
12213     game.set_centered_player = FALSE;
12214
12215     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12216     DrawGameDoorValues();
12217   }
12218
12219   for (i = 0; i < MAX_PLAYERS; i++)
12220   {
12221     int actual_player_action = stored_player[i].effective_action;
12222
12223 #if 1
12224     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12225        - rnd_equinox_tetrachloride 048
12226        - rnd_equinox_tetrachloride_ii 096
12227        - rnd_emanuel_schmieg 002
12228        - doctor_sloan_ww 001, 020
12229     */
12230     if (stored_player[i].MovPos == 0)
12231       CheckGravityMovement(&stored_player[i]);
12232 #endif
12233
12234     /* overwrite programmed action with tape action */
12235     if (stored_player[i].programmed_action)
12236       actual_player_action = stored_player[i].programmed_action;
12237
12238     PlayerActions(&stored_player[i], actual_player_action);
12239
12240     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12241   }
12242
12243   ScrollScreen(NULL, SCROLL_GO_ON);
12244
12245   /* for backwards compatibility, the following code emulates a fixed bug that
12246      occured when pushing elements (causing elements that just made their last
12247      pushing step to already (if possible) make their first falling step in the
12248      same game frame, which is bad); this code is also needed to use the famous
12249      "spring push bug" which is used in older levels and might be wanted to be
12250      used also in newer levels, but in this case the buggy pushing code is only
12251      affecting the "spring" element and no other elements */
12252
12253   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12254   {
12255     for (i = 0; i < MAX_PLAYERS; i++)
12256     {
12257       struct PlayerInfo *player = &stored_player[i];
12258       int x = player->jx;
12259       int y = player->jy;
12260
12261       if (player->active && player->is_pushing && player->is_moving &&
12262           IS_MOVING(x, y) &&
12263           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12264            Feld[x][y] == EL_SPRING))
12265       {
12266         ContinueMoving(x, y);
12267
12268         /* continue moving after pushing (this is actually a bug) */
12269         if (!IS_MOVING(x, y))
12270           Stop[x][y] = FALSE;
12271       }
12272     }
12273   }
12274
12275 #if 0
12276   debug_print_timestamp(0, "start main loop profiling");
12277 #endif
12278
12279   SCAN_PLAYFIELD(x, y)
12280   {
12281     ChangeCount[x][y] = 0;
12282     ChangeEvent[x][y] = -1;
12283
12284     /* this must be handled before main playfield loop */
12285     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12286     {
12287       MovDelay[x][y]--;
12288       if (MovDelay[x][y] <= 0)
12289         RemoveField(x, y);
12290     }
12291
12292 #if USE_NEW_SNAP_DELAY
12293     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12294     {
12295       MovDelay[x][y]--;
12296       if (MovDelay[x][y] <= 0)
12297       {
12298         RemoveField(x, y);
12299         TEST_DrawLevelField(x, y);
12300
12301         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12302       }
12303     }
12304 #endif
12305
12306 #if DEBUG
12307     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12308     {
12309       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12310       printf("GameActions(): This should never happen!\n");
12311
12312       ChangePage[x][y] = -1;
12313     }
12314 #endif
12315
12316     Stop[x][y] = FALSE;
12317     if (WasJustMoving[x][y] > 0)
12318       WasJustMoving[x][y]--;
12319     if (WasJustFalling[x][y] > 0)
12320       WasJustFalling[x][y]--;
12321     if (CheckCollision[x][y] > 0)
12322       CheckCollision[x][y]--;
12323     if (CheckImpact[x][y] > 0)
12324       CheckImpact[x][y]--;
12325
12326     GfxFrame[x][y]++;
12327
12328     /* reset finished pushing action (not done in ContinueMoving() to allow
12329        continuous pushing animation for elements with zero push delay) */
12330     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12331     {
12332       ResetGfxAnimation(x, y);
12333       TEST_DrawLevelField(x, y);
12334     }
12335
12336 #if DEBUG
12337     if (IS_BLOCKED(x, y))
12338     {
12339       int oldx, oldy;
12340
12341       Blocked2Moving(x, y, &oldx, &oldy);
12342       if (!IS_MOVING(oldx, oldy))
12343       {
12344         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12345         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12346         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12347         printf("GameActions(): This should never happen!\n");
12348       }
12349     }
12350 #endif
12351   }
12352
12353 #if 0
12354   debug_print_timestamp(0, "- time for pre-main loop:");
12355 #endif
12356
12357 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12358   SCAN_PLAYFIELD(x, y)
12359   {
12360     element = Feld[x][y];
12361     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12362
12363 #if 1
12364     {
12365 #if 1
12366       int element2 = element;
12367       int graphic2 = graphic;
12368 #else
12369       int element2 = Feld[x][y];
12370       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12371 #endif
12372       int last_gfx_frame = GfxFrame[x][y];
12373
12374       if (graphic_info[graphic2].anim_global_sync)
12375         GfxFrame[x][y] = FrameCounter;
12376       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12377         GfxFrame[x][y] = CustomValue[x][y];
12378       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12379         GfxFrame[x][y] = element_info[element2].collect_score;
12380       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12381         GfxFrame[x][y] = ChangeDelay[x][y];
12382
12383       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12384         DrawLevelGraphicAnimation(x, y, graphic2);
12385     }
12386 #else
12387     ResetGfxFrame(x, y, TRUE);
12388 #endif
12389
12390 #if 1
12391     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12392         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12393       ResetRandomAnimationValue(x, y);
12394 #endif
12395
12396 #if 1
12397     SetRandomAnimationValue(x, y);
12398 #endif
12399
12400 #if 1
12401     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12402 #endif
12403   }
12404 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12405
12406 #if 0
12407   debug_print_timestamp(0, "- time for TEST loop:     -->");
12408 #endif
12409
12410   SCAN_PLAYFIELD(x, y)
12411   {
12412     element = Feld[x][y];
12413     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12414
12415     ResetGfxFrame(x, y, TRUE);
12416
12417     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12418         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12419       ResetRandomAnimationValue(x, y);
12420
12421     SetRandomAnimationValue(x, y);
12422
12423     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12424
12425     if (IS_INACTIVE(element))
12426     {
12427       if (IS_ANIMATED(graphic))
12428         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12429
12430       continue;
12431     }
12432
12433     /* this may take place after moving, so 'element' may have changed */
12434     if (IS_CHANGING(x, y) &&
12435         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12436     {
12437       int page = element_info[element].event_page_nr[CE_DELAY];
12438
12439 #if 1
12440       HandleElementChange(x, y, page);
12441 #else
12442       if (CAN_CHANGE(element))
12443         HandleElementChange(x, y, page);
12444
12445       if (HAS_ACTION(element))
12446         ExecuteCustomElementAction(x, y, element, page);
12447 #endif
12448
12449       element = Feld[x][y];
12450       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12451     }
12452
12453 #if 0   // ---------------------------------------------------------------------
12454
12455     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12456     {
12457       StartMoving(x, y);
12458
12459       element = Feld[x][y];
12460       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12461
12462       if (IS_ANIMATED(graphic) &&
12463           !IS_MOVING(x, y) &&
12464           !Stop[x][y])
12465         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12466
12467       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12468         TEST_DrawTwinkleOnField(x, y);
12469     }
12470     else if (IS_MOVING(x, y))
12471       ContinueMoving(x, y);
12472     else
12473     {
12474       switch (element)
12475       {
12476         case EL_ACID:
12477         case EL_EXIT_OPEN:
12478         case EL_EM_EXIT_OPEN:
12479         case EL_SP_EXIT_OPEN:
12480         case EL_STEEL_EXIT_OPEN:
12481         case EL_EM_STEEL_EXIT_OPEN:
12482         case EL_SP_TERMINAL:
12483         case EL_SP_TERMINAL_ACTIVE:
12484         case EL_EXTRA_TIME:
12485         case EL_SHIELD_NORMAL:
12486         case EL_SHIELD_DEADLY:
12487           if (IS_ANIMATED(graphic))
12488             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12489           break;
12490
12491         case EL_DYNAMITE_ACTIVE:
12492         case EL_EM_DYNAMITE_ACTIVE:
12493         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12494         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12495         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12496         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12497         case EL_SP_DISK_RED_ACTIVE:
12498           CheckDynamite(x, y);
12499           break;
12500
12501         case EL_AMOEBA_GROWING:
12502           AmoebeWaechst(x, y);
12503           break;
12504
12505         case EL_AMOEBA_SHRINKING:
12506           AmoebaDisappearing(x, y);
12507           break;
12508
12509 #if !USE_NEW_AMOEBA_CODE
12510         case EL_AMOEBA_WET:
12511         case EL_AMOEBA_DRY:
12512         case EL_AMOEBA_FULL:
12513         case EL_BD_AMOEBA:
12514         case EL_EMC_DRIPPER:
12515           AmoebeAbleger(x, y);
12516           break;
12517 #endif
12518
12519         case EL_GAME_OF_LIFE:
12520         case EL_BIOMAZE:
12521           Life(x, y);
12522           break;
12523
12524         case EL_EXIT_CLOSED:
12525           CheckExit(x, y);
12526           break;
12527
12528         case EL_EM_EXIT_CLOSED:
12529           CheckExitEM(x, y);
12530           break;
12531
12532         case EL_STEEL_EXIT_CLOSED:
12533           CheckExitSteel(x, y);
12534           break;
12535
12536         case EL_EM_STEEL_EXIT_CLOSED:
12537           CheckExitSteelEM(x, y);
12538           break;
12539
12540         case EL_SP_EXIT_CLOSED:
12541           CheckExitSP(x, y);
12542           break;
12543
12544         case EL_EXPANDABLE_WALL_GROWING:
12545         case EL_EXPANDABLE_STEELWALL_GROWING:
12546           MauerWaechst(x, y);
12547           break;
12548
12549         case EL_EXPANDABLE_WALL:
12550         case EL_EXPANDABLE_WALL_HORIZONTAL:
12551         case EL_EXPANDABLE_WALL_VERTICAL:
12552         case EL_EXPANDABLE_WALL_ANY:
12553         case EL_BD_EXPANDABLE_WALL:
12554           MauerAbleger(x, y);
12555           break;
12556
12557         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12558         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12559         case EL_EXPANDABLE_STEELWALL_ANY:
12560           MauerAblegerStahl(x, y);
12561           break;
12562
12563         case EL_FLAMES:
12564           CheckForDragon(x, y);
12565           break;
12566
12567         case EL_EXPLOSION:
12568           break;
12569
12570         case EL_ELEMENT_SNAPPING:
12571         case EL_DIAGONAL_SHRINKING:
12572         case EL_DIAGONAL_GROWING:
12573         {
12574           graphic =
12575             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12576
12577           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12578           break;
12579         }
12580
12581         default:
12582           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12583             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12584           break;
12585       }
12586     }
12587
12588 #else   // ---------------------------------------------------------------------
12589
12590     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12591     {
12592       StartMoving(x, y);
12593
12594       element = Feld[x][y];
12595       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12596
12597       if (IS_ANIMATED(graphic) &&
12598           !IS_MOVING(x, y) &&
12599           !Stop[x][y])
12600         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12601
12602       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12603         TEST_DrawTwinkleOnField(x, y);
12604     }
12605     else if ((element == EL_ACID ||
12606               element == EL_EXIT_OPEN ||
12607               element == EL_EM_EXIT_OPEN ||
12608               element == EL_SP_EXIT_OPEN ||
12609               element == EL_STEEL_EXIT_OPEN ||
12610               element == EL_EM_STEEL_EXIT_OPEN ||
12611               element == EL_SP_TERMINAL ||
12612               element == EL_SP_TERMINAL_ACTIVE ||
12613               element == EL_EXTRA_TIME ||
12614               element == EL_SHIELD_NORMAL ||
12615               element == EL_SHIELD_DEADLY) &&
12616              IS_ANIMATED(graphic))
12617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12618     else if (IS_MOVING(x, y))
12619       ContinueMoving(x, y);
12620     else if (IS_ACTIVE_BOMB(element))
12621       CheckDynamite(x, y);
12622     else if (element == EL_AMOEBA_GROWING)
12623       AmoebeWaechst(x, y);
12624     else if (element == EL_AMOEBA_SHRINKING)
12625       AmoebaDisappearing(x, y);
12626
12627 #if !USE_NEW_AMOEBA_CODE
12628     else if (IS_AMOEBALIVE(element))
12629       AmoebeAbleger(x, y);
12630 #endif
12631
12632     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12633       Life(x, y);
12634     else if (element == EL_EXIT_CLOSED)
12635       CheckExit(x, y);
12636     else if (element == EL_EM_EXIT_CLOSED)
12637       CheckExitEM(x, y);
12638     else if (element == EL_STEEL_EXIT_CLOSED)
12639       CheckExitSteel(x, y);
12640     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12641       CheckExitSteelEM(x, y);
12642     else if (element == EL_SP_EXIT_CLOSED)
12643       CheckExitSP(x, y);
12644     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12645              element == EL_EXPANDABLE_STEELWALL_GROWING)
12646       MauerWaechst(x, y);
12647     else if (element == EL_EXPANDABLE_WALL ||
12648              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12649              element == EL_EXPANDABLE_WALL_VERTICAL ||
12650              element == EL_EXPANDABLE_WALL_ANY ||
12651              element == EL_BD_EXPANDABLE_WALL)
12652       MauerAbleger(x, y);
12653     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12654              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12655              element == EL_EXPANDABLE_STEELWALL_ANY)
12656       MauerAblegerStahl(x, y);
12657     else if (element == EL_FLAMES)
12658       CheckForDragon(x, y);
12659     else if (element == EL_EXPLOSION)
12660       ; /* drawing of correct explosion animation is handled separately */
12661     else if (element == EL_ELEMENT_SNAPPING ||
12662              element == EL_DIAGONAL_SHRINKING ||
12663              element == EL_DIAGONAL_GROWING)
12664     {
12665       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12666
12667       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12668     }
12669     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12670       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12671
12672 #endif  // ---------------------------------------------------------------------
12673
12674     if (IS_BELT_ACTIVE(element))
12675       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12676
12677     if (game.magic_wall_active)
12678     {
12679       int jx = local_player->jx, jy = local_player->jy;
12680
12681       /* play the element sound at the position nearest to the player */
12682       if ((element == EL_MAGIC_WALL_FULL ||
12683            element == EL_MAGIC_WALL_ACTIVE ||
12684            element == EL_MAGIC_WALL_EMPTYING ||
12685            element == EL_BD_MAGIC_WALL_FULL ||
12686            element == EL_BD_MAGIC_WALL_ACTIVE ||
12687            element == EL_BD_MAGIC_WALL_EMPTYING ||
12688            element == EL_DC_MAGIC_WALL_FULL ||
12689            element == EL_DC_MAGIC_WALL_ACTIVE ||
12690            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12691           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12692       {
12693         magic_wall_x = x;
12694         magic_wall_y = y;
12695       }
12696     }
12697   }
12698
12699 #if 0
12700   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12701 #endif
12702
12703 #if USE_NEW_AMOEBA_CODE
12704   /* new experimental amoeba growth stuff */
12705   if (!(FrameCounter % 8))
12706   {
12707     static unsigned long random = 1684108901;
12708
12709     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12710     {
12711       x = RND(lev_fieldx);
12712       y = RND(lev_fieldy);
12713       element = Feld[x][y];
12714
12715       if (!IS_PLAYER(x,y) &&
12716           (element == EL_EMPTY ||
12717            CAN_GROW_INTO(element) ||
12718            element == EL_QUICKSAND_EMPTY ||
12719            element == EL_QUICKSAND_FAST_EMPTY ||
12720            element == EL_ACID_SPLASH_LEFT ||
12721            element == EL_ACID_SPLASH_RIGHT))
12722       {
12723         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12724             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12725             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12726             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12727           Feld[x][y] = EL_AMOEBA_DROP;
12728       }
12729
12730       random = random * 129 + 1;
12731     }
12732   }
12733 #endif
12734
12735 #if 0
12736   if (game.explosions_delayed)
12737 #endif
12738   {
12739     game.explosions_delayed = FALSE;
12740
12741     SCAN_PLAYFIELD(x, y)
12742     {
12743       element = Feld[x][y];
12744
12745       if (ExplodeField[x][y])
12746         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12747       else if (element == EL_EXPLOSION)
12748         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12749
12750       ExplodeField[x][y] = EX_TYPE_NONE;
12751     }
12752
12753     game.explosions_delayed = TRUE;
12754   }
12755
12756   if (game.magic_wall_active)
12757   {
12758     if (!(game.magic_wall_time_left % 4))
12759     {
12760       int element = Feld[magic_wall_x][magic_wall_y];
12761
12762       if (element == EL_BD_MAGIC_WALL_FULL ||
12763           element == EL_BD_MAGIC_WALL_ACTIVE ||
12764           element == EL_BD_MAGIC_WALL_EMPTYING)
12765         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12766       else if (element == EL_DC_MAGIC_WALL_FULL ||
12767                element == EL_DC_MAGIC_WALL_ACTIVE ||
12768                element == EL_DC_MAGIC_WALL_EMPTYING)
12769         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12770       else
12771         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12772     }
12773
12774     if (game.magic_wall_time_left > 0)
12775     {
12776       game.magic_wall_time_left--;
12777
12778       if (!game.magic_wall_time_left)
12779       {
12780         SCAN_PLAYFIELD(x, y)
12781         {
12782           element = Feld[x][y];
12783
12784           if (element == EL_MAGIC_WALL_ACTIVE ||
12785               element == EL_MAGIC_WALL_FULL)
12786           {
12787             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12788             TEST_DrawLevelField(x, y);
12789           }
12790           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12791                    element == EL_BD_MAGIC_WALL_FULL)
12792           {
12793             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12794             TEST_DrawLevelField(x, y);
12795           }
12796           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12797                    element == EL_DC_MAGIC_WALL_FULL)
12798           {
12799             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12800             TEST_DrawLevelField(x, y);
12801           }
12802         }
12803
12804         game.magic_wall_active = FALSE;
12805       }
12806     }
12807   }
12808
12809   if (game.light_time_left > 0)
12810   {
12811     game.light_time_left--;
12812
12813     if (game.light_time_left == 0)
12814       RedrawAllLightSwitchesAndInvisibleElements();
12815   }
12816
12817   if (game.timegate_time_left > 0)
12818   {
12819     game.timegate_time_left--;
12820
12821     if (game.timegate_time_left == 0)
12822       CloseAllOpenTimegates();
12823   }
12824
12825   if (game.lenses_time_left > 0)
12826   {
12827     game.lenses_time_left--;
12828
12829     if (game.lenses_time_left == 0)
12830       RedrawAllInvisibleElementsForLenses();
12831   }
12832
12833   if (game.magnify_time_left > 0)
12834   {
12835     game.magnify_time_left--;
12836
12837     if (game.magnify_time_left == 0)
12838       RedrawAllInvisibleElementsForMagnifier();
12839   }
12840
12841   for (i = 0; i < MAX_PLAYERS; i++)
12842   {
12843     struct PlayerInfo *player = &stored_player[i];
12844
12845     if (SHIELD_ON(player))
12846     {
12847       if (player->shield_deadly_time_left)
12848         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12849       else if (player->shield_normal_time_left)
12850         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12851     }
12852   }
12853
12854 #if USE_DELAYED_GFX_REDRAW
12855   SCAN_PLAYFIELD(x, y)
12856   {
12857 #if 1
12858     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12859 #else
12860     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12861         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12862 #endif
12863     {
12864       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12865          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12866
12867       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12868         DrawLevelField(x, y);
12869
12870       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12871         DrawLevelFieldCrumbledSand(x, y);
12872
12873       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12874         DrawLevelFieldCrumbledSandNeighbours(x, y);
12875
12876       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12877         DrawTwinkleOnField(x, y);
12878     }
12879
12880     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12881   }
12882 #endif
12883
12884   CheckLevelTime();
12885
12886   DrawAllPlayers();
12887   PlayAllPlayersSound();
12888
12889   if (options.debug)                    /* calculate frames per second */
12890   {
12891     static unsigned long fps_counter = 0;
12892     static int fps_frames = 0;
12893     unsigned long fps_delay_ms = Counter() - fps_counter;
12894
12895     fps_frames++;
12896
12897     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12898     {
12899       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12900
12901       fps_frames = 0;
12902       fps_counter = Counter();
12903     }
12904
12905     redraw_mask |= REDRAW_FPS;
12906   }
12907
12908   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12909
12910   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12911   {
12912     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12913
12914     local_player->show_envelope = 0;
12915   }
12916
12917 #if 0
12918   debug_print_timestamp(0, "stop main loop profiling ");
12919   printf("----------------------------------------------------------\n");
12920 #endif
12921
12922   /* use random number generator in every frame to make it less predictable */
12923   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12924     RND(1);
12925 }
12926
12927 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12928 {
12929   int min_x = x, min_y = y, max_x = x, max_y = y;
12930   int i;
12931
12932   for (i = 0; i < MAX_PLAYERS; i++)
12933   {
12934     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12935
12936     if (!stored_player[i].active || &stored_player[i] == player)
12937       continue;
12938
12939     min_x = MIN(min_x, jx);
12940     min_y = MIN(min_y, jy);
12941     max_x = MAX(max_x, jx);
12942     max_y = MAX(max_y, jy);
12943   }
12944
12945   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12946 }
12947
12948 static boolean AllPlayersInVisibleScreen()
12949 {
12950   int i;
12951
12952   for (i = 0; i < MAX_PLAYERS; i++)
12953   {
12954     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12955
12956     if (!stored_player[i].active)
12957       continue;
12958
12959     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12960       return FALSE;
12961   }
12962
12963   return TRUE;
12964 }
12965
12966 void ScrollLevel(int dx, int dy)
12967 {
12968 #if 0
12969   /* (directly solved in BlitBitmap() now) */
12970   static Bitmap *bitmap_db_field2 = NULL;
12971   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12972   int x, y;
12973 #else
12974   int x, y;
12975 #endif
12976
12977 #if 0
12978   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12979   /* only horizontal XOR vertical scroll direction allowed */
12980   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12981     return;
12982 #endif
12983
12984 #if 0
12985   /* (directly solved in BlitBitmap() now) */
12986   if (bitmap_db_field2 == NULL)
12987     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12988
12989   /* needed when blitting directly to same bitmap -- should not be needed with
12990      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12991   BlitBitmap(drawto_field, bitmap_db_field2,
12992              FX + TILEX * (dx == -1) - softscroll_offset,
12993              FY + TILEY * (dy == -1) - softscroll_offset,
12994              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12995              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12996              FX + TILEX * (dx == 1) - softscroll_offset,
12997              FY + TILEY * (dy == 1) - softscroll_offset);
12998   BlitBitmap(bitmap_db_field2, drawto_field,
12999              FX + TILEX * (dx == 1) - softscroll_offset,
13000              FY + TILEY * (dy == 1) - softscroll_offset,
13001              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13002              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13003              FX + TILEX * (dx == 1) - softscroll_offset,
13004              FY + TILEY * (dy == 1) - softscroll_offset);
13005
13006 #else
13007
13008 #if 0
13009   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13010   int xsize = (BX2 - BX1 + 1);
13011   int ysize = (BY2 - BY1 + 1);
13012   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13013   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13014   int step  = (start < end ? +1 : -1);
13015
13016   for (i = start; i != end; i += step)
13017   {
13018     BlitBitmap(drawto_field, drawto_field,
13019                FX + TILEX * (dx != 0 ? i + step : 0),
13020                FY + TILEY * (dy != 0 ? i + step : 0),
13021                TILEX * (dx != 0 ? 1 : xsize),
13022                TILEY * (dy != 0 ? 1 : ysize),
13023                FX + TILEX * (dx != 0 ? i : 0),
13024                FY + TILEY * (dy != 0 ? i : 0));
13025   }
13026
13027 #else
13028
13029   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13030
13031   BlitBitmap(drawto_field, drawto_field,
13032              FX + TILEX * (dx == -1) - softscroll_offset,
13033              FY + TILEY * (dy == -1) - softscroll_offset,
13034              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13035              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13036              FX + TILEX * (dx == 1) - softscroll_offset,
13037              FY + TILEY * (dy == 1) - softscroll_offset);
13038 #endif
13039 #endif
13040
13041   if (dx != 0)
13042   {
13043     x = (dx == 1 ? BX1 : BX2);
13044     for (y = BY1; y <= BY2; y++)
13045       DrawScreenField(x, y);
13046   }
13047
13048   if (dy != 0)
13049   {
13050     y = (dy == 1 ? BY1 : BY2);
13051     for (x = BX1; x <= BX2; x++)
13052       DrawScreenField(x, y);
13053   }
13054
13055   redraw_mask |= REDRAW_FIELD;
13056 }
13057
13058 static boolean canFallDown(struct PlayerInfo *player)
13059 {
13060   int jx = player->jx, jy = player->jy;
13061
13062   return (IN_LEV_FIELD(jx, jy + 1) &&
13063           (IS_FREE(jx, jy + 1) ||
13064            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13065           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13066           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13067 }
13068
13069 static boolean canPassField(int x, int y, int move_dir)
13070 {
13071   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13072   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13073   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13074   int nextx = x + dx;
13075   int nexty = y + dy;
13076   int element = Feld[x][y];
13077
13078   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13079           !CAN_MOVE(element) &&
13080           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13081           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13082           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13083 }
13084
13085 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13086 {
13087   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13088   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13089   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13090   int newx = x + dx;
13091   int newy = y + dy;
13092
13093   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13094           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13095           (IS_DIGGABLE(Feld[newx][newy]) ||
13096            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13097            canPassField(newx, newy, move_dir)));
13098 }
13099
13100 static void CheckGravityMovement(struct PlayerInfo *player)
13101 {
13102 #if USE_PLAYER_GRAVITY
13103   if (player->gravity && !player->programmed_action)
13104 #else
13105   if (game.gravity && !player->programmed_action)
13106 #endif
13107   {
13108     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13109     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13110     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13111     int jx = player->jx, jy = player->jy;
13112     boolean player_is_moving_to_valid_field =
13113       (!player_is_snapping &&
13114        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13115         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13116     boolean player_can_fall_down = canFallDown(player);
13117
13118     if (player_can_fall_down &&
13119         !player_is_moving_to_valid_field)
13120       player->programmed_action = MV_DOWN;
13121   }
13122 }
13123
13124 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13125 {
13126   return CheckGravityMovement(player);
13127
13128 #if USE_PLAYER_GRAVITY
13129   if (player->gravity && !player->programmed_action)
13130 #else
13131   if (game.gravity && !player->programmed_action)
13132 #endif
13133   {
13134     int jx = player->jx, jy = player->jy;
13135     boolean field_under_player_is_free =
13136       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13137     boolean player_is_standing_on_valid_field =
13138       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13139        (IS_WALKABLE(Feld[jx][jy]) &&
13140         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13141
13142     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13143       player->programmed_action = MV_DOWN;
13144   }
13145 }
13146
13147 /*
13148   MovePlayerOneStep()
13149   -----------------------------------------------------------------------------
13150   dx, dy:               direction (non-diagonal) to try to move the player to
13151   real_dx, real_dy:     direction as read from input device (can be diagonal)
13152 */
13153
13154 boolean MovePlayerOneStep(struct PlayerInfo *player,
13155                           int dx, int dy, int real_dx, int real_dy)
13156 {
13157   int jx = player->jx, jy = player->jy;
13158   int new_jx = jx + dx, new_jy = jy + dy;
13159 #if !USE_FIXED_DONT_RUN_INTO
13160   int element;
13161 #endif
13162   int can_move;
13163   boolean player_can_move = !player->cannot_move;
13164
13165   if (!player->active || (!dx && !dy))
13166     return MP_NO_ACTION;
13167
13168   player->MovDir = (dx < 0 ? MV_LEFT :
13169                     dx > 0 ? MV_RIGHT :
13170                     dy < 0 ? MV_UP :
13171                     dy > 0 ? MV_DOWN :  MV_NONE);
13172
13173   if (!IN_LEV_FIELD(new_jx, new_jy))
13174     return MP_NO_ACTION;
13175
13176   if (!player_can_move)
13177   {
13178     if (player->MovPos == 0)
13179     {
13180       player->is_moving = FALSE;
13181       player->is_digging = FALSE;
13182       player->is_collecting = FALSE;
13183       player->is_snapping = FALSE;
13184       player->is_pushing = FALSE;
13185     }
13186   }
13187
13188 #if 1
13189   if (!options.network && game.centered_player_nr == -1 &&
13190       !AllPlayersInSight(player, new_jx, new_jy))
13191     return MP_NO_ACTION;
13192 #else
13193   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13194     return MP_NO_ACTION;
13195 #endif
13196
13197 #if !USE_FIXED_DONT_RUN_INTO
13198   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13199
13200   /* (moved to DigField()) */
13201   if (player_can_move && DONT_RUN_INTO(element))
13202   {
13203     if (element == EL_ACID && dx == 0 && dy == 1)
13204     {
13205       SplashAcid(new_jx, new_jy);
13206       Feld[jx][jy] = EL_PLAYER_1;
13207       InitMovingField(jx, jy, MV_DOWN);
13208       Store[jx][jy] = EL_ACID;
13209       ContinueMoving(jx, jy);
13210       BuryPlayer(player);
13211     }
13212     else
13213       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13214
13215     return MP_MOVING;
13216   }
13217 #endif
13218
13219   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13220   if (can_move != MP_MOVING)
13221     return can_move;
13222
13223   /* check if DigField() has caused relocation of the player */
13224   if (player->jx != jx || player->jy != jy)
13225     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13226
13227   StorePlayer[jx][jy] = 0;
13228   player->last_jx = jx;
13229   player->last_jy = jy;
13230   player->jx = new_jx;
13231   player->jy = new_jy;
13232   StorePlayer[new_jx][new_jy] = player->element_nr;
13233
13234   if (player->move_delay_value_next != -1)
13235   {
13236     player->move_delay_value = player->move_delay_value_next;
13237     player->move_delay_value_next = -1;
13238   }
13239
13240   player->MovPos =
13241     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13242
13243   player->step_counter++;
13244
13245   PlayerVisit[jx][jy] = FrameCounter;
13246
13247 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13248   player->is_moving = TRUE;
13249 #endif
13250
13251 #if 1
13252   /* should better be called in MovePlayer(), but this breaks some tapes */
13253   ScrollPlayer(player, SCROLL_INIT);
13254 #endif
13255
13256   return MP_MOVING;
13257 }
13258
13259 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13260 {
13261   int jx = player->jx, jy = player->jy;
13262   int old_jx = jx, old_jy = jy;
13263   int moved = MP_NO_ACTION;
13264
13265   if (!player->active)
13266     return FALSE;
13267
13268   if (!dx && !dy)
13269   {
13270     if (player->MovPos == 0)
13271     {
13272       player->is_moving = FALSE;
13273       player->is_digging = FALSE;
13274       player->is_collecting = FALSE;
13275       player->is_snapping = FALSE;
13276       player->is_pushing = FALSE;
13277     }
13278
13279     return FALSE;
13280   }
13281
13282   if (player->move_delay > 0)
13283     return FALSE;
13284
13285   player->move_delay = -1;              /* set to "uninitialized" value */
13286
13287   /* store if player is automatically moved to next field */
13288   player->is_auto_moving = (player->programmed_action != MV_NONE);
13289
13290   /* remove the last programmed player action */
13291   player->programmed_action = 0;
13292
13293   if (player->MovPos)
13294   {
13295     /* should only happen if pre-1.2 tape recordings are played */
13296     /* this is only for backward compatibility */
13297
13298     int original_move_delay_value = player->move_delay_value;
13299
13300 #if DEBUG
13301     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13302            tape.counter);
13303 #endif
13304
13305     /* scroll remaining steps with finest movement resolution */
13306     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13307
13308     while (player->MovPos)
13309     {
13310       ScrollPlayer(player, SCROLL_GO_ON);
13311       ScrollScreen(NULL, SCROLL_GO_ON);
13312
13313       AdvanceFrameAndPlayerCounters(player->index_nr);
13314
13315       DrawAllPlayers();
13316       BackToFront();
13317     }
13318
13319     player->move_delay_value = original_move_delay_value;
13320   }
13321
13322   player->is_active = FALSE;
13323
13324   if (player->last_move_dir & MV_HORIZONTAL)
13325   {
13326     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13327       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13328   }
13329   else
13330   {
13331     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13332       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13333   }
13334
13335 #if USE_FIXED_BORDER_RUNNING_GFX
13336   if (!moved && !player->is_active)
13337   {
13338     player->is_moving = FALSE;
13339     player->is_digging = FALSE;
13340     player->is_collecting = FALSE;
13341     player->is_snapping = FALSE;
13342     player->is_pushing = FALSE;
13343   }
13344 #endif
13345
13346   jx = player->jx;
13347   jy = player->jy;
13348
13349 #if 1
13350   if (moved & MP_MOVING && !ScreenMovPos &&
13351       (player->index_nr == game.centered_player_nr ||
13352        game.centered_player_nr == -1))
13353 #else
13354   if (moved & MP_MOVING && !ScreenMovPos &&
13355       (player == local_player || !options.network))
13356 #endif
13357   {
13358     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13359     int offset = game.scroll_delay_value;
13360
13361     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13362     {
13363       /* actual player has left the screen -- scroll in that direction */
13364       if (jx != old_jx)         /* player has moved horizontally */
13365         scroll_x += (jx - old_jx);
13366       else                      /* player has moved vertically */
13367         scroll_y += (jy - old_jy);
13368     }
13369     else
13370     {
13371       if (jx != old_jx)         /* player has moved horizontally */
13372       {
13373         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13374             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13375           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13376
13377         /* don't scroll over playfield boundaries */
13378         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13379           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13380
13381         /* don't scroll more than one field at a time */
13382         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13383
13384         /* don't scroll against the player's moving direction */
13385         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13386             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13387           scroll_x = old_scroll_x;
13388       }
13389       else                      /* player has moved vertically */
13390       {
13391         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13392             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13393           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13394
13395         /* don't scroll over playfield boundaries */
13396         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13397           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13398
13399         /* don't scroll more than one field at a time */
13400         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13401
13402         /* don't scroll against the player's moving direction */
13403         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13404             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13405           scroll_y = old_scroll_y;
13406       }
13407     }
13408
13409     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13410     {
13411 #if 1
13412       if (!options.network && game.centered_player_nr == -1 &&
13413           !AllPlayersInVisibleScreen())
13414       {
13415         scroll_x = old_scroll_x;
13416         scroll_y = old_scroll_y;
13417       }
13418       else
13419 #else
13420       if (!options.network && !AllPlayersInVisibleScreen())
13421       {
13422         scroll_x = old_scroll_x;
13423         scroll_y = old_scroll_y;
13424       }
13425       else
13426 #endif
13427       {
13428         ScrollScreen(player, SCROLL_INIT);
13429         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13430       }
13431     }
13432   }
13433
13434   player->StepFrame = 0;
13435
13436   if (moved & MP_MOVING)
13437   {
13438     if (old_jx != jx && old_jy == jy)
13439       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13440     else if (old_jx == jx && old_jy != jy)
13441       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13442
13443     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13444
13445     player->last_move_dir = player->MovDir;
13446     player->is_moving = TRUE;
13447     player->is_snapping = FALSE;
13448     player->is_switching = FALSE;
13449     player->is_dropping = FALSE;
13450     player->is_dropping_pressed = FALSE;
13451     player->drop_pressed_delay = 0;
13452
13453 #if 0
13454     /* should better be called here than above, but this breaks some tapes */
13455     ScrollPlayer(player, SCROLL_INIT);
13456 #endif
13457   }
13458   else
13459   {
13460     CheckGravityMovementWhenNotMoving(player);
13461
13462     player->is_moving = FALSE;
13463
13464     /* at this point, the player is allowed to move, but cannot move right now
13465        (e.g. because of something blocking the way) -- ensure that the player
13466        is also allowed to move in the next frame (in old versions before 3.1.1,
13467        the player was forced to wait again for eight frames before next try) */
13468
13469     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13470       player->move_delay = 0;   /* allow direct movement in the next frame */
13471   }
13472
13473   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13474     player->move_delay = player->move_delay_value;
13475
13476   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13477   {
13478     TestIfPlayerTouchesBadThing(jx, jy);
13479     TestIfPlayerTouchesCustomElement(jx, jy);
13480   }
13481
13482   if (!player->active)
13483     RemovePlayer(player);
13484
13485   return moved;
13486 }
13487
13488 void ScrollPlayer(struct PlayerInfo *player, int mode)
13489 {
13490   int jx = player->jx, jy = player->jy;
13491   int last_jx = player->last_jx, last_jy = player->last_jy;
13492   int move_stepsize = TILEX / player->move_delay_value;
13493
13494 #if USE_NEW_PLAYER_SPEED
13495   if (!player->active)
13496     return;
13497
13498   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13499     return;
13500 #else
13501   if (!player->active || player->MovPos == 0)
13502     return;
13503 #endif
13504
13505   if (mode == SCROLL_INIT)
13506   {
13507     player->actual_frame_counter = FrameCounter;
13508     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13509
13510     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13511         Feld[last_jx][last_jy] == EL_EMPTY)
13512     {
13513       int last_field_block_delay = 0;   /* start with no blocking at all */
13514       int block_delay_adjustment = player->block_delay_adjustment;
13515
13516       /* if player blocks last field, add delay for exactly one move */
13517       if (player->block_last_field)
13518       {
13519         last_field_block_delay += player->move_delay_value;
13520
13521         /* when blocking enabled, prevent moving up despite gravity */
13522 #if USE_PLAYER_GRAVITY
13523         if (player->gravity && player->MovDir == MV_UP)
13524           block_delay_adjustment = -1;
13525 #else
13526         if (game.gravity && player->MovDir == MV_UP)
13527           block_delay_adjustment = -1;
13528 #endif
13529       }
13530
13531       /* add block delay adjustment (also possible when not blocking) */
13532       last_field_block_delay += block_delay_adjustment;
13533
13534       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13535       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13536     }
13537
13538 #if USE_NEW_PLAYER_SPEED
13539     if (player->MovPos != 0)    /* player has not yet reached destination */
13540       return;
13541 #else
13542     return;
13543 #endif
13544   }
13545   else if (!FrameReached(&player->actual_frame_counter, 1))
13546     return;
13547
13548 #if USE_NEW_PLAYER_SPEED
13549   if (player->MovPos != 0)
13550   {
13551     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13552     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13553
13554     /* before DrawPlayer() to draw correct player graphic for this case */
13555     if (player->MovPos == 0)
13556       CheckGravityMovement(player);
13557   }
13558 #else
13559   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13560   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13561
13562   /* before DrawPlayer() to draw correct player graphic for this case */
13563   if (player->MovPos == 0)
13564     CheckGravityMovement(player);
13565 #endif
13566
13567   if (player->MovPos == 0)      /* player reached destination field */
13568   {
13569     if (player->move_delay_reset_counter > 0)
13570     {
13571       player->move_delay_reset_counter--;
13572
13573       if (player->move_delay_reset_counter == 0)
13574       {
13575         /* continue with normal speed after quickly moving through gate */
13576         HALVE_PLAYER_SPEED(player);
13577
13578         /* be able to make the next move without delay */
13579         player->move_delay = 0;
13580       }
13581     }
13582
13583     player->last_jx = jx;
13584     player->last_jy = jy;
13585
13586     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13587         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13588         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13589         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13590         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13591         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13592     {
13593       DrawPlayer(player);       /* needed here only to cleanup last field */
13594       RemovePlayer(player);
13595
13596       if (local_player->friends_still_needed == 0 ||
13597           IS_SP_ELEMENT(Feld[jx][jy]))
13598         PlayerWins(player);
13599     }
13600
13601     /* this breaks one level: "machine", level 000 */
13602     {
13603       int move_direction = player->MovDir;
13604       int enter_side = MV_DIR_OPPOSITE(move_direction);
13605       int leave_side = move_direction;
13606       int old_jx = last_jx;
13607       int old_jy = last_jy;
13608       int old_element = Feld[old_jx][old_jy];
13609       int new_element = Feld[jx][jy];
13610
13611       if (IS_CUSTOM_ELEMENT(old_element))
13612         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13613                                    CE_LEFT_BY_PLAYER,
13614                                    player->index_bit, leave_side);
13615
13616       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13617                                           CE_PLAYER_LEAVES_X,
13618                                           player->index_bit, leave_side);
13619
13620       if (IS_CUSTOM_ELEMENT(new_element))
13621         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13622                                    player->index_bit, enter_side);
13623
13624       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13625                                           CE_PLAYER_ENTERS_X,
13626                                           player->index_bit, enter_side);
13627
13628 #if USE_FIX_CE_ACTION_WITH_PLAYER
13629       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13630                                         CE_MOVE_OF_X, move_direction);
13631 #else
13632       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13633                                         CE_MOVE_OF_X, move_direction);
13634 #endif
13635     }
13636
13637     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13638     {
13639       TestIfPlayerTouchesBadThing(jx, jy);
13640       TestIfPlayerTouchesCustomElement(jx, jy);
13641
13642       /* needed because pushed element has not yet reached its destination,
13643          so it would trigger a change event at its previous field location */
13644       if (!player->is_pushing)
13645         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13646
13647       if (!player->active)
13648         RemovePlayer(player);
13649     }
13650
13651     if (!local_player->LevelSolved && level.use_step_counter)
13652     {
13653       int i;
13654
13655       TimePlayed++;
13656
13657       if (TimeLeft > 0)
13658       {
13659         TimeLeft--;
13660
13661         if (TimeLeft <= 10 && setup.time_limit)
13662           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13663
13664 #if 1
13665         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13666
13667         DisplayGameControlValues();
13668 #else
13669         DrawGameValue_Time(TimeLeft);
13670 #endif
13671
13672         if (!TimeLeft && setup.time_limit)
13673           for (i = 0; i < MAX_PLAYERS; i++)
13674             KillPlayer(&stored_player[i]);
13675       }
13676 #if 1
13677       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13678       {
13679         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13680
13681         DisplayGameControlValues();
13682       }
13683 #else
13684       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13685         DrawGameValue_Time(TimePlayed);
13686 #endif
13687     }
13688
13689     if (tape.single_step && tape.recording && !tape.pausing &&
13690         !player->programmed_action)
13691       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13692   }
13693 }
13694
13695 void ScrollScreen(struct PlayerInfo *player, int mode)
13696 {
13697   static unsigned long screen_frame_counter = 0;
13698
13699   if (mode == SCROLL_INIT)
13700   {
13701     /* set scrolling step size according to actual player's moving speed */
13702     ScrollStepSize = TILEX / player->move_delay_value;
13703
13704     screen_frame_counter = FrameCounter;
13705     ScreenMovDir = player->MovDir;
13706     ScreenMovPos = player->MovPos;
13707     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13708     return;
13709   }
13710   else if (!FrameReached(&screen_frame_counter, 1))
13711     return;
13712
13713   if (ScreenMovPos)
13714   {
13715     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13716     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13717     redraw_mask |= REDRAW_FIELD;
13718   }
13719   else
13720     ScreenMovDir = MV_NONE;
13721 }
13722
13723 void TestIfPlayerTouchesCustomElement(int x, int y)
13724 {
13725   static int xy[4][2] =
13726   {
13727     { 0, -1 },
13728     { -1, 0 },
13729     { +1, 0 },
13730     { 0, +1 }
13731   };
13732   static int trigger_sides[4][2] =
13733   {
13734     /* center side       border side */
13735     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13736     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13737     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13738     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13739   };
13740   static int touch_dir[4] =
13741   {
13742     MV_LEFT | MV_RIGHT,
13743     MV_UP   | MV_DOWN,
13744     MV_UP   | MV_DOWN,
13745     MV_LEFT | MV_RIGHT
13746   };
13747   int center_element = Feld[x][y];      /* should always be non-moving! */
13748   int i;
13749
13750   for (i = 0; i < NUM_DIRECTIONS; i++)
13751   {
13752     int xx = x + xy[i][0];
13753     int yy = y + xy[i][1];
13754     int center_side = trigger_sides[i][0];
13755     int border_side = trigger_sides[i][1];
13756     int border_element;
13757
13758     if (!IN_LEV_FIELD(xx, yy))
13759       continue;
13760
13761     if (IS_PLAYER(x, y))                /* player found at center element */
13762     {
13763       struct PlayerInfo *player = PLAYERINFO(x, y);
13764
13765       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13766         border_element = Feld[xx][yy];          /* may be moving! */
13767       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13768         border_element = Feld[xx][yy];
13769       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13770         border_element = MovingOrBlocked2Element(xx, yy);
13771       else
13772         continue;               /* center and border element do not touch */
13773
13774       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13775                                  player->index_bit, border_side);
13776       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13777                                           CE_PLAYER_TOUCHES_X,
13778                                           player->index_bit, border_side);
13779
13780 #if USE_FIX_CE_ACTION_WITH_PLAYER
13781       {
13782         /* use player element that is initially defined in the level playfield,
13783            not the player element that corresponds to the runtime player number
13784            (example: a level that contains EL_PLAYER_3 as the only player would
13785            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13786         int player_element = PLAYERINFO(x, y)->initial_element;
13787
13788         CheckElementChangeBySide(xx, yy, border_element, player_element,
13789                                  CE_TOUCHING_X, border_side);
13790       }
13791 #endif
13792     }
13793     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13794     {
13795       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13796
13797       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13798       {
13799         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13800           continue;             /* center and border element do not touch */
13801       }
13802
13803       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13804                                  player->index_bit, center_side);
13805       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13806                                           CE_PLAYER_TOUCHES_X,
13807                                           player->index_bit, center_side);
13808
13809 #if USE_FIX_CE_ACTION_WITH_PLAYER
13810       {
13811         /* use player element that is initially defined in the level playfield,
13812            not the player element that corresponds to the runtime player number
13813            (example: a level that contains EL_PLAYER_3 as the only player would
13814            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13815         int player_element = PLAYERINFO(xx, yy)->initial_element;
13816
13817         CheckElementChangeBySide(x, y, center_element, player_element,
13818                                  CE_TOUCHING_X, center_side);
13819       }
13820 #endif
13821
13822       break;
13823     }
13824   }
13825 }
13826
13827 #if USE_ELEMENT_TOUCHING_BUGFIX
13828
13829 void TestIfElementTouchesCustomElement(int x, int y)
13830 {
13831   static int xy[4][2] =
13832   {
13833     { 0, -1 },
13834     { -1, 0 },
13835     { +1, 0 },
13836     { 0, +1 }
13837   };
13838   static int trigger_sides[4][2] =
13839   {
13840     /* center side      border side */
13841     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13842     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13843     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13844     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13845   };
13846   static int touch_dir[4] =
13847   {
13848     MV_LEFT | MV_RIGHT,
13849     MV_UP   | MV_DOWN,
13850     MV_UP   | MV_DOWN,
13851     MV_LEFT | MV_RIGHT
13852   };
13853   boolean change_center_element = FALSE;
13854   int center_element = Feld[x][y];      /* should always be non-moving! */
13855   int border_element_old[NUM_DIRECTIONS];
13856   int i;
13857
13858   for (i = 0; i < NUM_DIRECTIONS; i++)
13859   {
13860     int xx = x + xy[i][0];
13861     int yy = y + xy[i][1];
13862     int border_element;
13863
13864     border_element_old[i] = -1;
13865
13866     if (!IN_LEV_FIELD(xx, yy))
13867       continue;
13868
13869     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13870       border_element = Feld[xx][yy];    /* may be moving! */
13871     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13872       border_element = Feld[xx][yy];
13873     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13874       border_element = MovingOrBlocked2Element(xx, yy);
13875     else
13876       continue;                 /* center and border element do not touch */
13877
13878     border_element_old[i] = border_element;
13879   }
13880
13881   for (i = 0; i < NUM_DIRECTIONS; i++)
13882   {
13883     int xx = x + xy[i][0];
13884     int yy = y + xy[i][1];
13885     int center_side = trigger_sides[i][0];
13886     int border_element = border_element_old[i];
13887
13888     if (border_element == -1)
13889       continue;
13890
13891     /* check for change of border element */
13892     CheckElementChangeBySide(xx, yy, border_element, center_element,
13893                              CE_TOUCHING_X, center_side);
13894
13895     /* (center element cannot be player, so we dont have to check this here) */
13896   }
13897
13898   for (i = 0; i < NUM_DIRECTIONS; i++)
13899   {
13900     int xx = x + xy[i][0];
13901     int yy = y + xy[i][1];
13902     int border_side = trigger_sides[i][1];
13903     int border_element = border_element_old[i];
13904
13905     if (border_element == -1)
13906       continue;
13907
13908     /* check for change of center element (but change it only once) */
13909     if (!change_center_element)
13910       change_center_element =
13911         CheckElementChangeBySide(x, y, center_element, border_element,
13912                                  CE_TOUCHING_X, border_side);
13913
13914 #if USE_FIX_CE_ACTION_WITH_PLAYER
13915     if (IS_PLAYER(xx, yy))
13916     {
13917       /* use player element that is initially defined in the level playfield,
13918          not the player element that corresponds to the runtime player number
13919          (example: a level that contains EL_PLAYER_3 as the only player would
13920          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13921       int player_element = PLAYERINFO(xx, yy)->initial_element;
13922
13923       CheckElementChangeBySide(x, y, center_element, player_element,
13924                                CE_TOUCHING_X, border_side);
13925     }
13926 #endif
13927   }
13928 }
13929
13930 #else
13931
13932 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13933 {
13934   static int xy[4][2] =
13935   {
13936     { 0, -1 },
13937     { -1, 0 },
13938     { +1, 0 },
13939     { 0, +1 }
13940   };
13941   static int trigger_sides[4][2] =
13942   {
13943     /* center side      border side */
13944     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13945     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13946     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13947     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13948   };
13949   static int touch_dir[4] =
13950   {
13951     MV_LEFT | MV_RIGHT,
13952     MV_UP   | MV_DOWN,
13953     MV_UP   | MV_DOWN,
13954     MV_LEFT | MV_RIGHT
13955   };
13956   boolean change_center_element = FALSE;
13957   int center_element = Feld[x][y];      /* should always be non-moving! */
13958   int i;
13959
13960   for (i = 0; i < NUM_DIRECTIONS; i++)
13961   {
13962     int xx = x + xy[i][0];
13963     int yy = y + xy[i][1];
13964     int center_side = trigger_sides[i][0];
13965     int border_side = trigger_sides[i][1];
13966     int border_element;
13967
13968     if (!IN_LEV_FIELD(xx, yy))
13969       continue;
13970
13971     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13972       border_element = Feld[xx][yy];    /* may be moving! */
13973     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13974       border_element = Feld[xx][yy];
13975     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13976       border_element = MovingOrBlocked2Element(xx, yy);
13977     else
13978       continue;                 /* center and border element do not touch */
13979
13980     /* check for change of center element (but change it only once) */
13981     if (!change_center_element)
13982       change_center_element =
13983         CheckElementChangeBySide(x, y, center_element, border_element,
13984                                  CE_TOUCHING_X, border_side);
13985
13986     /* check for change of border element */
13987     CheckElementChangeBySide(xx, yy, border_element, center_element,
13988                              CE_TOUCHING_X, center_side);
13989   }
13990 }
13991
13992 #endif
13993
13994 void TestIfElementHitsCustomElement(int x, int y, int direction)
13995 {
13996   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13997   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13998   int hitx = x + dx, hity = y + dy;
13999   int hitting_element = Feld[x][y];
14000   int touched_element;
14001
14002   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14003     return;
14004
14005   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14006                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14007
14008   if (IN_LEV_FIELD(hitx, hity))
14009   {
14010     int opposite_direction = MV_DIR_OPPOSITE(direction);
14011     int hitting_side = direction;
14012     int touched_side = opposite_direction;
14013     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14014                           MovDir[hitx][hity] != direction ||
14015                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14016
14017     object_hit = TRUE;
14018
14019     if (object_hit)
14020     {
14021       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14022                                CE_HITTING_X, touched_side);
14023
14024       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14025                                CE_HIT_BY_X, hitting_side);
14026
14027       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14028                                CE_HIT_BY_SOMETHING, opposite_direction);
14029
14030 #if USE_FIX_CE_ACTION_WITH_PLAYER
14031       if (IS_PLAYER(hitx, hity))
14032       {
14033         /* use player element that is initially defined in the level playfield,
14034            not the player element that corresponds to the runtime player number
14035            (example: a level that contains EL_PLAYER_3 as the only player would
14036            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14037         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14038
14039         CheckElementChangeBySide(x, y, hitting_element, player_element,
14040                                  CE_HITTING_X, touched_side);
14041       }
14042 #endif
14043     }
14044   }
14045
14046   /* "hitting something" is also true when hitting the playfield border */
14047   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14048                            CE_HITTING_SOMETHING, direction);
14049 }
14050
14051 #if 0
14052 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14053 {
14054   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14055   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14056   int hitx = x + dx, hity = y + dy;
14057   int hitting_element = Feld[x][y];
14058   int touched_element;
14059 #if 0
14060   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14061                         !IS_FREE(hitx, hity) &&
14062                         (!IS_MOVING(hitx, hity) ||
14063                          MovDir[hitx][hity] != direction ||
14064                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14065 #endif
14066
14067   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14068     return;
14069
14070 #if 0
14071   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14072     return;
14073 #endif
14074
14075   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14076                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14077
14078   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14079                            EP_CAN_SMASH_EVERYTHING, direction);
14080
14081   if (IN_LEV_FIELD(hitx, hity))
14082   {
14083     int opposite_direction = MV_DIR_OPPOSITE(direction);
14084     int hitting_side = direction;
14085     int touched_side = opposite_direction;
14086 #if 0
14087     int touched_element = MovingOrBlocked2Element(hitx, hity);
14088 #endif
14089 #if 1
14090     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14091                           MovDir[hitx][hity] != direction ||
14092                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14093
14094     object_hit = TRUE;
14095 #endif
14096
14097     if (object_hit)
14098     {
14099       int i;
14100
14101       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14102                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14103
14104       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14105                                CE_OTHER_IS_SMASHING, touched_side);
14106
14107       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14108                                CE_OTHER_GETS_SMASHED, hitting_side);
14109     }
14110   }
14111 }
14112 #endif
14113
14114 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14115 {
14116   int i, kill_x = -1, kill_y = -1;
14117
14118   int bad_element = -1;
14119   static int test_xy[4][2] =
14120   {
14121     { 0, -1 },
14122     { -1, 0 },
14123     { +1, 0 },
14124     { 0, +1 }
14125   };
14126   static int test_dir[4] =
14127   {
14128     MV_UP,
14129     MV_LEFT,
14130     MV_RIGHT,
14131     MV_DOWN
14132   };
14133
14134   for (i = 0; i < NUM_DIRECTIONS; i++)
14135   {
14136     int test_x, test_y, test_move_dir, test_element;
14137
14138     test_x = good_x + test_xy[i][0];
14139     test_y = good_y + test_xy[i][1];
14140
14141     if (!IN_LEV_FIELD(test_x, test_y))
14142       continue;
14143
14144     test_move_dir =
14145       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14146
14147     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14148
14149     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14150        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14151     */
14152     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14153         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14154     {
14155       kill_x = test_x;
14156       kill_y = test_y;
14157       bad_element = test_element;
14158
14159       break;
14160     }
14161   }
14162
14163   if (kill_x != -1 || kill_y != -1)
14164   {
14165     if (IS_PLAYER(good_x, good_y))
14166     {
14167       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14168
14169       if (player->shield_deadly_time_left > 0 &&
14170           !IS_INDESTRUCTIBLE(bad_element))
14171         Bang(kill_x, kill_y);
14172       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14173         KillPlayer(player);
14174     }
14175     else
14176       Bang(good_x, good_y);
14177   }
14178 }
14179
14180 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14181 {
14182   int i, kill_x = -1, kill_y = -1;
14183   int bad_element = Feld[bad_x][bad_y];
14184   static int test_xy[4][2] =
14185   {
14186     { 0, -1 },
14187     { -1, 0 },
14188     { +1, 0 },
14189     { 0, +1 }
14190   };
14191   static int touch_dir[4] =
14192   {
14193     MV_LEFT | MV_RIGHT,
14194     MV_UP   | MV_DOWN,
14195     MV_UP   | MV_DOWN,
14196     MV_LEFT | MV_RIGHT
14197   };
14198   static int test_dir[4] =
14199   {
14200     MV_UP,
14201     MV_LEFT,
14202     MV_RIGHT,
14203     MV_DOWN
14204   };
14205
14206   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14207     return;
14208
14209   for (i = 0; i < NUM_DIRECTIONS; i++)
14210   {
14211     int test_x, test_y, test_move_dir, test_element;
14212
14213     test_x = bad_x + test_xy[i][0];
14214     test_y = bad_y + test_xy[i][1];
14215
14216     if (!IN_LEV_FIELD(test_x, test_y))
14217       continue;
14218
14219     test_move_dir =
14220       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14221
14222     test_element = Feld[test_x][test_y];
14223
14224     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14225        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14226     */
14227     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14228         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14229     {
14230       /* good thing is player or penguin that does not move away */
14231       if (IS_PLAYER(test_x, test_y))
14232       {
14233         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14234
14235         if (bad_element == EL_ROBOT && player->is_moving)
14236           continue;     /* robot does not kill player if he is moving */
14237
14238         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14239         {
14240           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14241             continue;           /* center and border element do not touch */
14242         }
14243
14244         kill_x = test_x;
14245         kill_y = test_y;
14246
14247         break;
14248       }
14249       else if (test_element == EL_PENGUIN)
14250       {
14251         kill_x = test_x;
14252         kill_y = test_y;
14253
14254         break;
14255       }
14256     }
14257   }
14258
14259   if (kill_x != -1 || kill_y != -1)
14260   {
14261     if (IS_PLAYER(kill_x, kill_y))
14262     {
14263       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14264
14265       if (player->shield_deadly_time_left > 0 &&
14266           !IS_INDESTRUCTIBLE(bad_element))
14267         Bang(bad_x, bad_y);
14268       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14269         KillPlayer(player);
14270     }
14271     else
14272       Bang(kill_x, kill_y);
14273   }
14274 }
14275
14276 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14277 {
14278   int bad_element = Feld[bad_x][bad_y];
14279   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14280   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14281   int test_x = bad_x + dx, test_y = bad_y + dy;
14282   int test_move_dir, test_element;
14283   int kill_x = -1, kill_y = -1;
14284
14285   if (!IN_LEV_FIELD(test_x, test_y))
14286     return;
14287
14288   test_move_dir =
14289     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14290
14291   test_element = Feld[test_x][test_y];
14292
14293   if (test_move_dir != bad_move_dir)
14294   {
14295     /* good thing can be player or penguin that does not move away */
14296     if (IS_PLAYER(test_x, test_y))
14297     {
14298       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14299
14300       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14301          player as being hit when he is moving towards the bad thing, because
14302          the "get hit by" condition would be lost after the player stops) */
14303       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14304         return;         /* player moves away from bad thing */
14305
14306       kill_x = test_x;
14307       kill_y = test_y;
14308     }
14309     else if (test_element == EL_PENGUIN)
14310     {
14311       kill_x = test_x;
14312       kill_y = test_y;
14313     }
14314   }
14315
14316   if (kill_x != -1 || kill_y != -1)
14317   {
14318     if (IS_PLAYER(kill_x, kill_y))
14319     {
14320       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14321
14322       if (player->shield_deadly_time_left > 0 &&
14323           !IS_INDESTRUCTIBLE(bad_element))
14324         Bang(bad_x, bad_y);
14325       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14326         KillPlayer(player);
14327     }
14328     else
14329       Bang(kill_x, kill_y);
14330   }
14331 }
14332
14333 void TestIfPlayerTouchesBadThing(int x, int y)
14334 {
14335   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14336 }
14337
14338 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14339 {
14340   TestIfGoodThingHitsBadThing(x, y, move_dir);
14341 }
14342
14343 void TestIfBadThingTouchesPlayer(int x, int y)
14344 {
14345   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14346 }
14347
14348 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14349 {
14350   TestIfBadThingHitsGoodThing(x, y, move_dir);
14351 }
14352
14353 void TestIfFriendTouchesBadThing(int x, int y)
14354 {
14355   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14356 }
14357
14358 void TestIfBadThingTouchesFriend(int x, int y)
14359 {
14360   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14361 }
14362
14363 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14364 {
14365   int i, kill_x = bad_x, kill_y = bad_y;
14366   static int xy[4][2] =
14367   {
14368     { 0, -1 },
14369     { -1, 0 },
14370     { +1, 0 },
14371     { 0, +1 }
14372   };
14373
14374   for (i = 0; i < NUM_DIRECTIONS; i++)
14375   {
14376     int x, y, element;
14377
14378     x = bad_x + xy[i][0];
14379     y = bad_y + xy[i][1];
14380     if (!IN_LEV_FIELD(x, y))
14381       continue;
14382
14383     element = Feld[x][y];
14384     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14385         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14386     {
14387       kill_x = x;
14388       kill_y = y;
14389       break;
14390     }
14391   }
14392
14393   if (kill_x != bad_x || kill_y != bad_y)
14394     Bang(bad_x, bad_y);
14395 }
14396
14397 void KillPlayer(struct PlayerInfo *player)
14398 {
14399   int jx = player->jx, jy = player->jy;
14400
14401   if (!player->active)
14402     return;
14403
14404 #if 0
14405   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14406          player->killed, player->active, player->reanimated);
14407 #endif
14408
14409   /* the following code was introduced to prevent an infinite loop when calling
14410      -> Bang()
14411      -> CheckTriggeredElementChangeExt()
14412      -> ExecuteCustomElementAction()
14413      -> KillPlayer()
14414      -> (infinitely repeating the above sequence of function calls)
14415      which occurs when killing the player while having a CE with the setting
14416      "kill player X when explosion of <player X>"; the solution using a new
14417      field "player->killed" was chosen for backwards compatibility, although
14418      clever use of the fields "player->active" etc. would probably also work */
14419 #if 1
14420   if (player->killed)
14421     return;
14422 #endif
14423
14424   player->killed = TRUE;
14425
14426   /* remove accessible field at the player's position */
14427   Feld[jx][jy] = EL_EMPTY;
14428
14429   /* deactivate shield (else Bang()/Explode() would not work right) */
14430   player->shield_normal_time_left = 0;
14431   player->shield_deadly_time_left = 0;
14432
14433 #if 0
14434   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14435          player->killed, player->active, player->reanimated);
14436 #endif
14437
14438   Bang(jx, jy);
14439
14440 #if 0
14441   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14442          player->killed, player->active, player->reanimated);
14443 #endif
14444
14445 #if USE_PLAYER_REANIMATION
14446 #if 1
14447   if (player->reanimated)       /* killed player may have been reanimated */
14448     player->killed = player->reanimated = FALSE;
14449   else
14450     BuryPlayer(player);
14451 #else
14452   if (player->killed)           /* player may have been reanimated */
14453     BuryPlayer(player);
14454 #endif
14455 #else
14456   BuryPlayer(player);
14457 #endif
14458 }
14459
14460 static void KillPlayerUnlessEnemyProtected(int x, int y)
14461 {
14462   if (!PLAYER_ENEMY_PROTECTED(x, y))
14463     KillPlayer(PLAYERINFO(x, y));
14464 }
14465
14466 static void KillPlayerUnlessExplosionProtected(int x, int y)
14467 {
14468   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14469     KillPlayer(PLAYERINFO(x, y));
14470 }
14471
14472 void BuryPlayer(struct PlayerInfo *player)
14473 {
14474   int jx = player->jx, jy = player->jy;
14475
14476   if (!player->active)
14477     return;
14478
14479   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14480   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14481
14482   player->GameOver = TRUE;
14483   RemovePlayer(player);
14484 }
14485
14486 void RemovePlayer(struct PlayerInfo *player)
14487 {
14488   int jx = player->jx, jy = player->jy;
14489   int i, found = FALSE;
14490
14491   player->present = FALSE;
14492   player->active = FALSE;
14493
14494   if (!ExplodeField[jx][jy])
14495     StorePlayer[jx][jy] = 0;
14496
14497   if (player->is_moving)
14498     TEST_DrawLevelField(player->last_jx, player->last_jy);
14499
14500   for (i = 0; i < MAX_PLAYERS; i++)
14501     if (stored_player[i].active)
14502       found = TRUE;
14503
14504   if (!found)
14505     AllPlayersGone = TRUE;
14506
14507   ExitX = ZX = jx;
14508   ExitY = ZY = jy;
14509 }
14510
14511 #if USE_NEW_SNAP_DELAY
14512 static void setFieldForSnapping(int x, int y, int element, int direction)
14513 {
14514   struct ElementInfo *ei = &element_info[element];
14515   int direction_bit = MV_DIR_TO_BIT(direction);
14516   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14517   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14518                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14519
14520   Feld[x][y] = EL_ELEMENT_SNAPPING;
14521   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14522
14523   ResetGfxAnimation(x, y);
14524
14525   GfxElement[x][y] = element;
14526   GfxAction[x][y] = action;
14527   GfxDir[x][y] = direction;
14528   GfxFrame[x][y] = -1;
14529 }
14530 #endif
14531
14532 /*
14533   =============================================================================
14534   checkDiagonalPushing()
14535   -----------------------------------------------------------------------------
14536   check if diagonal input device direction results in pushing of object
14537   (by checking if the alternative direction is walkable, diggable, ...)
14538   =============================================================================
14539 */
14540
14541 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14542                                     int x, int y, int real_dx, int real_dy)
14543 {
14544   int jx, jy, dx, dy, xx, yy;
14545
14546   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14547     return TRUE;
14548
14549   /* diagonal direction: check alternative direction */
14550   jx = player->jx;
14551   jy = player->jy;
14552   dx = x - jx;
14553   dy = y - jy;
14554   xx = jx + (dx == 0 ? real_dx : 0);
14555   yy = jy + (dy == 0 ? real_dy : 0);
14556
14557   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14558 }
14559
14560 /*
14561   =============================================================================
14562   DigField()
14563   -----------------------------------------------------------------------------
14564   x, y:                 field next to player (non-diagonal) to try to dig to
14565   real_dx, real_dy:     direction as read from input device (can be diagonal)
14566   =============================================================================
14567 */
14568
14569 static int DigField(struct PlayerInfo *player,
14570                     int oldx, int oldy, int x, int y,
14571                     int real_dx, int real_dy, int mode)
14572 {
14573   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14574   boolean player_was_pushing = player->is_pushing;
14575   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14576   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14577   int jx = oldx, jy = oldy;
14578   int dx = x - jx, dy = y - jy;
14579   int nextx = x + dx, nexty = y + dy;
14580   int move_direction = (dx == -1 ? MV_LEFT  :
14581                         dx == +1 ? MV_RIGHT :
14582                         dy == -1 ? MV_UP    :
14583                         dy == +1 ? MV_DOWN  : MV_NONE);
14584   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14585   int dig_side = MV_DIR_OPPOSITE(move_direction);
14586   int old_element = Feld[jx][jy];
14587 #if USE_FIXED_DONT_RUN_INTO
14588   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14589 #else
14590   int element;
14591 #endif
14592   int collect_count;
14593
14594   if (is_player)                /* function can also be called by EL_PENGUIN */
14595   {
14596     if (player->MovPos == 0)
14597     {
14598       player->is_digging = FALSE;
14599       player->is_collecting = FALSE;
14600     }
14601
14602     if (player->MovPos == 0)    /* last pushing move finished */
14603       player->is_pushing = FALSE;
14604
14605     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14606     {
14607       player->is_switching = FALSE;
14608       player->push_delay = -1;
14609
14610       return MP_NO_ACTION;
14611     }
14612   }
14613
14614 #if !USE_FIXED_DONT_RUN_INTO
14615   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14616     return MP_NO_ACTION;
14617 #endif
14618
14619   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14620     old_element = Back[jx][jy];
14621
14622   /* in case of element dropped at player position, check background */
14623   else if (Back[jx][jy] != EL_EMPTY &&
14624            game.engine_version >= VERSION_IDENT(2,2,0,0))
14625     old_element = Back[jx][jy];
14626
14627   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14628     return MP_NO_ACTION;        /* field has no opening in this direction */
14629
14630   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14631     return MP_NO_ACTION;        /* field has no opening in this direction */
14632
14633 #if USE_FIXED_DONT_RUN_INTO
14634   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14635   {
14636     SplashAcid(x, y);
14637
14638     Feld[jx][jy] = player->artwork_element;
14639     InitMovingField(jx, jy, MV_DOWN);
14640     Store[jx][jy] = EL_ACID;
14641     ContinueMoving(jx, jy);
14642     BuryPlayer(player);
14643
14644     return MP_DONT_RUN_INTO;
14645   }
14646 #endif
14647
14648 #if USE_FIXED_DONT_RUN_INTO
14649   if (player_can_move && DONT_RUN_INTO(element))
14650   {
14651     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14652
14653     return MP_DONT_RUN_INTO;
14654   }
14655 #endif
14656
14657 #if USE_FIXED_DONT_RUN_INTO
14658   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14659     return MP_NO_ACTION;
14660 #endif
14661
14662 #if !USE_FIXED_DONT_RUN_INTO
14663   element = Feld[x][y];
14664 #endif
14665
14666   collect_count = element_info[element].collect_count_initial;
14667
14668   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14669     return MP_NO_ACTION;
14670
14671   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14672     player_can_move = player_can_move_or_snap;
14673
14674   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14675       game.engine_version >= VERSION_IDENT(2,2,0,0))
14676   {
14677     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14678                                player->index_bit, dig_side);
14679     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14680                                         player->index_bit, dig_side);
14681
14682     if (element == EL_DC_LANDMINE)
14683       Bang(x, y);
14684
14685     if (Feld[x][y] != element)          /* field changed by snapping */
14686       return MP_ACTION;
14687
14688     return MP_NO_ACTION;
14689   }
14690
14691 #if USE_PLAYER_GRAVITY
14692   if (player->gravity && is_player && !player->is_auto_moving &&
14693       canFallDown(player) && move_direction != MV_DOWN &&
14694       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14695     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14696 #else
14697   if (game.gravity && is_player && !player->is_auto_moving &&
14698       canFallDown(player) && move_direction != MV_DOWN &&
14699       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14700     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14701 #endif
14702
14703   if (player_can_move &&
14704       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14705   {
14706     int sound_element = SND_ELEMENT(element);
14707     int sound_action = ACTION_WALKING;
14708
14709     if (IS_RND_GATE(element))
14710     {
14711       if (!player->key[RND_GATE_NR(element)])
14712         return MP_NO_ACTION;
14713     }
14714     else if (IS_RND_GATE_GRAY(element))
14715     {
14716       if (!player->key[RND_GATE_GRAY_NR(element)])
14717         return MP_NO_ACTION;
14718     }
14719     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14720     {
14721       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14722         return MP_NO_ACTION;
14723     }
14724     else if (element == EL_EXIT_OPEN ||
14725              element == EL_EM_EXIT_OPEN ||
14726              element == EL_STEEL_EXIT_OPEN ||
14727              element == EL_EM_STEEL_EXIT_OPEN ||
14728              element == EL_SP_EXIT_OPEN ||
14729              element == EL_SP_EXIT_OPENING)
14730     {
14731       sound_action = ACTION_PASSING;    /* player is passing exit */
14732     }
14733     else if (element == EL_EMPTY)
14734     {
14735       sound_action = ACTION_MOVING;             /* nothing to walk on */
14736     }
14737
14738     /* play sound from background or player, whatever is available */
14739     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14740       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14741     else
14742       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14743   }
14744   else if (player_can_move &&
14745            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14746   {
14747     if (!ACCESS_FROM(element, opposite_direction))
14748       return MP_NO_ACTION;      /* field not accessible from this direction */
14749
14750     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14751       return MP_NO_ACTION;
14752
14753     if (IS_EM_GATE(element))
14754     {
14755       if (!player->key[EM_GATE_NR(element)])
14756         return MP_NO_ACTION;
14757     }
14758     else if (IS_EM_GATE_GRAY(element))
14759     {
14760       if (!player->key[EM_GATE_GRAY_NR(element)])
14761         return MP_NO_ACTION;
14762     }
14763     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14764     {
14765       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14766         return MP_NO_ACTION;
14767     }
14768     else if (IS_EMC_GATE(element))
14769     {
14770       if (!player->key[EMC_GATE_NR(element)])
14771         return MP_NO_ACTION;
14772     }
14773     else if (IS_EMC_GATE_GRAY(element))
14774     {
14775       if (!player->key[EMC_GATE_GRAY_NR(element)])
14776         return MP_NO_ACTION;
14777     }
14778     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14779     {
14780       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14781         return MP_NO_ACTION;
14782     }
14783     else if (element == EL_DC_GATE_WHITE ||
14784              element == EL_DC_GATE_WHITE_GRAY ||
14785              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14786     {
14787       if (player->num_white_keys == 0)
14788         return MP_NO_ACTION;
14789
14790       player->num_white_keys--;
14791     }
14792     else if (IS_SP_PORT(element))
14793     {
14794       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14795           element == EL_SP_GRAVITY_PORT_RIGHT ||
14796           element == EL_SP_GRAVITY_PORT_UP ||
14797           element == EL_SP_GRAVITY_PORT_DOWN)
14798 #if USE_PLAYER_GRAVITY
14799         player->gravity = !player->gravity;
14800 #else
14801         game.gravity = !game.gravity;
14802 #endif
14803       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14804                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14805                element == EL_SP_GRAVITY_ON_PORT_UP ||
14806                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14807 #if USE_PLAYER_GRAVITY
14808         player->gravity = TRUE;
14809 #else
14810         game.gravity = TRUE;
14811 #endif
14812       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14813                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14814                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14815                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14816 #if USE_PLAYER_GRAVITY
14817         player->gravity = FALSE;
14818 #else
14819         game.gravity = FALSE;
14820 #endif
14821     }
14822
14823     /* automatically move to the next field with double speed */
14824     player->programmed_action = move_direction;
14825
14826     if (player->move_delay_reset_counter == 0)
14827     {
14828       player->move_delay_reset_counter = 2;     /* two double speed steps */
14829
14830       DOUBLE_PLAYER_SPEED(player);
14831     }
14832
14833     PlayLevelSoundAction(x, y, ACTION_PASSING);
14834   }
14835   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14836   {
14837     RemoveField(x, y);
14838
14839     if (mode != DF_SNAP)
14840     {
14841       GfxElement[x][y] = GFX_ELEMENT(element);
14842       player->is_digging = TRUE;
14843     }
14844
14845     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14846
14847     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14848                                         player->index_bit, dig_side);
14849
14850     if (mode == DF_SNAP)
14851     {
14852 #if USE_NEW_SNAP_DELAY
14853       if (level.block_snap_field)
14854         setFieldForSnapping(x, y, element, move_direction);
14855       else
14856         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14857 #else
14858       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14859 #endif
14860
14861       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14862                                           player->index_bit, dig_side);
14863     }
14864   }
14865   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14866   {
14867     RemoveField(x, y);
14868
14869     if (is_player && mode != DF_SNAP)
14870     {
14871       GfxElement[x][y] = element;
14872       player->is_collecting = TRUE;
14873     }
14874
14875     if (element == EL_SPEED_PILL)
14876     {
14877       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14878     }
14879     else if (element == EL_EXTRA_TIME && level.time > 0)
14880     {
14881       TimeLeft += level.extra_time;
14882
14883 #if 1
14884       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14885
14886       DisplayGameControlValues();
14887 #else
14888       DrawGameValue_Time(TimeLeft);
14889 #endif
14890     }
14891     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14892     {
14893       player->shield_normal_time_left += level.shield_normal_time;
14894       if (element == EL_SHIELD_DEADLY)
14895         player->shield_deadly_time_left += level.shield_deadly_time;
14896     }
14897     else if (element == EL_DYNAMITE ||
14898              element == EL_EM_DYNAMITE ||
14899              element == EL_SP_DISK_RED)
14900     {
14901       if (player->inventory_size < MAX_INVENTORY_SIZE)
14902         player->inventory_element[player->inventory_size++] = element;
14903
14904       DrawGameDoorValues();
14905     }
14906     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14907     {
14908       player->dynabomb_count++;
14909       player->dynabombs_left++;
14910     }
14911     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14912     {
14913       player->dynabomb_size++;
14914     }
14915     else if (element == EL_DYNABOMB_INCREASE_POWER)
14916     {
14917       player->dynabomb_xl = TRUE;
14918     }
14919     else if (IS_KEY(element))
14920     {
14921       player->key[KEY_NR(element)] = TRUE;
14922
14923       DrawGameDoorValues();
14924     }
14925     else if (element == EL_DC_KEY_WHITE)
14926     {
14927       player->num_white_keys++;
14928
14929       /* display white keys? */
14930       /* DrawGameDoorValues(); */
14931     }
14932     else if (IS_ENVELOPE(element))
14933     {
14934       player->show_envelope = element;
14935     }
14936     else if (element == EL_EMC_LENSES)
14937     {
14938       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14939
14940       RedrawAllInvisibleElementsForLenses();
14941     }
14942     else if (element == EL_EMC_MAGNIFIER)
14943     {
14944       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14945
14946       RedrawAllInvisibleElementsForMagnifier();
14947     }
14948     else if (IS_DROPPABLE(element) ||
14949              IS_THROWABLE(element))     /* can be collected and dropped */
14950     {
14951       int i;
14952
14953       if (collect_count == 0)
14954         player->inventory_infinite_element = element;
14955       else
14956         for (i = 0; i < collect_count; i++)
14957           if (player->inventory_size < MAX_INVENTORY_SIZE)
14958             player->inventory_element[player->inventory_size++] = element;
14959
14960       DrawGameDoorValues();
14961     }
14962     else if (collect_count > 0)
14963     {
14964       local_player->gems_still_needed -= collect_count;
14965       if (local_player->gems_still_needed < 0)
14966         local_player->gems_still_needed = 0;
14967
14968 #if 1
14969       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14970
14971       DisplayGameControlValues();
14972 #else
14973       DrawGameValue_Emeralds(local_player->gems_still_needed);
14974 #endif
14975     }
14976
14977     RaiseScoreElement(element);
14978     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14979
14980     if (is_player)
14981       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14982                                           player->index_bit, dig_side);
14983
14984     if (mode == DF_SNAP)
14985     {
14986 #if USE_NEW_SNAP_DELAY
14987       if (level.block_snap_field)
14988         setFieldForSnapping(x, y, element, move_direction);
14989       else
14990         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14991 #else
14992       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14993 #endif
14994
14995       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14996                                           player->index_bit, dig_side);
14997     }
14998   }
14999   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15000   {
15001     if (mode == DF_SNAP && element != EL_BD_ROCK)
15002       return MP_NO_ACTION;
15003
15004     if (CAN_FALL(element) && dy)
15005       return MP_NO_ACTION;
15006
15007     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15008         !(element == EL_SPRING && level.use_spring_bug))
15009       return MP_NO_ACTION;
15010
15011     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15012         ((move_direction & MV_VERTICAL &&
15013           ((element_info[element].move_pattern & MV_LEFT &&
15014             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15015            (element_info[element].move_pattern & MV_RIGHT &&
15016             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15017          (move_direction & MV_HORIZONTAL &&
15018           ((element_info[element].move_pattern & MV_UP &&
15019             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15020            (element_info[element].move_pattern & MV_DOWN &&
15021             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15022       return MP_NO_ACTION;
15023
15024     /* do not push elements already moving away faster than player */
15025     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15026         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15027       return MP_NO_ACTION;
15028
15029     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15030     {
15031       if (player->push_delay_value == -1 || !player_was_pushing)
15032         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15033     }
15034     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15035     {
15036       if (player->push_delay_value == -1)
15037         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15038     }
15039     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15040     {
15041       if (!player->is_pushing)
15042         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15043     }
15044
15045     player->is_pushing = TRUE;
15046     player->is_active = TRUE;
15047
15048     if (!(IN_LEV_FIELD(nextx, nexty) &&
15049           (IS_FREE(nextx, nexty) ||
15050            (IS_SB_ELEMENT(element) &&
15051             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15052            (IS_CUSTOM_ELEMENT(element) &&
15053             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15054       return MP_NO_ACTION;
15055
15056     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15057       return MP_NO_ACTION;
15058
15059     if (player->push_delay == -1)       /* new pushing; restart delay */
15060       player->push_delay = 0;
15061
15062     if (player->push_delay < player->push_delay_value &&
15063         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15064         element != EL_SPRING && element != EL_BALLOON)
15065     {
15066       /* make sure that there is no move delay before next try to push */
15067       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15068         player->move_delay = 0;
15069
15070       return MP_NO_ACTION;
15071     }
15072
15073     if (IS_CUSTOM_ELEMENT(element) &&
15074         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15075     {
15076       if (!DigFieldByCE(nextx, nexty, element))
15077         return MP_NO_ACTION;
15078     }
15079
15080     if (IS_SB_ELEMENT(element))
15081     {
15082       if (element == EL_SOKOBAN_FIELD_FULL)
15083       {
15084         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15085         local_player->sokobanfields_still_needed++;
15086       }
15087
15088       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15089       {
15090         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15091         local_player->sokobanfields_still_needed--;
15092       }
15093
15094       Feld[x][y] = EL_SOKOBAN_OBJECT;
15095
15096       if (Back[x][y] == Back[nextx][nexty])
15097         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15098       else if (Back[x][y] != 0)
15099         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15100                                     ACTION_EMPTYING);
15101       else
15102         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15103                                     ACTION_FILLING);
15104
15105       if (local_player->sokobanfields_still_needed == 0 &&
15106           game.emulation == EMU_SOKOBAN)
15107       {
15108         PlayerWins(player);
15109
15110         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15111       }
15112     }
15113     else
15114       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15115
15116     InitMovingField(x, y, move_direction);
15117     GfxAction[x][y] = ACTION_PUSHING;
15118
15119     if (mode == DF_SNAP)
15120       ContinueMoving(x, y);
15121     else
15122       MovPos[x][y] = (dx != 0 ? dx : dy);
15123
15124     Pushed[x][y] = TRUE;
15125     Pushed[nextx][nexty] = TRUE;
15126
15127     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15128       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15129     else
15130       player->push_delay_value = -1;    /* get new value later */
15131
15132     /* check for element change _after_ element has been pushed */
15133     if (game.use_change_when_pushing_bug)
15134     {
15135       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15136                                  player->index_bit, dig_side);
15137       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15138                                           player->index_bit, dig_side);
15139     }
15140   }
15141   else if (IS_SWITCHABLE(element))
15142   {
15143     if (PLAYER_SWITCHING(player, x, y))
15144     {
15145       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15146                                           player->index_bit, dig_side);
15147
15148       return MP_ACTION;
15149     }
15150
15151     player->is_switching = TRUE;
15152     player->switch_x = x;
15153     player->switch_y = y;
15154
15155     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15156
15157     if (element == EL_ROBOT_WHEEL)
15158     {
15159       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15160       ZX = x;
15161       ZY = y;
15162
15163       game.robot_wheel_active = TRUE;
15164
15165       TEST_DrawLevelField(x, y);
15166     }
15167     else if (element == EL_SP_TERMINAL)
15168     {
15169       int xx, yy;
15170
15171       SCAN_PLAYFIELD(xx, yy)
15172       {
15173         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15174           Bang(xx, yy);
15175         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15176           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15177       }
15178     }
15179     else if (IS_BELT_SWITCH(element))
15180     {
15181       ToggleBeltSwitch(x, y);
15182     }
15183     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15184              element == EL_SWITCHGATE_SWITCH_DOWN ||
15185              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15186              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15187     {
15188       ToggleSwitchgateSwitch(x, y);
15189     }
15190     else if (element == EL_LIGHT_SWITCH ||
15191              element == EL_LIGHT_SWITCH_ACTIVE)
15192     {
15193       ToggleLightSwitch(x, y);
15194     }
15195     else if (element == EL_TIMEGATE_SWITCH ||
15196              element == EL_DC_TIMEGATE_SWITCH)
15197     {
15198       ActivateTimegateSwitch(x, y);
15199     }
15200     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15201              element == EL_BALLOON_SWITCH_RIGHT ||
15202              element == EL_BALLOON_SWITCH_UP    ||
15203              element == EL_BALLOON_SWITCH_DOWN  ||
15204              element == EL_BALLOON_SWITCH_NONE  ||
15205              element == EL_BALLOON_SWITCH_ANY)
15206     {
15207       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15208                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15209                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15210                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15211                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15212                              move_direction);
15213     }
15214     else if (element == EL_LAMP)
15215     {
15216       Feld[x][y] = EL_LAMP_ACTIVE;
15217       local_player->lights_still_needed--;
15218
15219       ResetGfxAnimation(x, y);
15220       TEST_DrawLevelField(x, y);
15221     }
15222     else if (element == EL_TIME_ORB_FULL)
15223     {
15224       Feld[x][y] = EL_TIME_ORB_EMPTY;
15225
15226       if (level.time > 0 || level.use_time_orb_bug)
15227       {
15228         TimeLeft += level.time_orb_time;
15229
15230 #if 1
15231         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15232
15233         DisplayGameControlValues();
15234 #else
15235         DrawGameValue_Time(TimeLeft);
15236 #endif
15237       }
15238
15239       ResetGfxAnimation(x, y);
15240       TEST_DrawLevelField(x, y);
15241     }
15242     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15243              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15244     {
15245       int xx, yy;
15246
15247       game.ball_state = !game.ball_state;
15248
15249       SCAN_PLAYFIELD(xx, yy)
15250       {
15251         int e = Feld[xx][yy];
15252
15253         if (game.ball_state)
15254         {
15255           if (e == EL_EMC_MAGIC_BALL)
15256             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15257           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15258             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15259         }
15260         else
15261         {
15262           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15263             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15264           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15265             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15266         }
15267       }
15268     }
15269
15270     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15271                                         player->index_bit, dig_side);
15272
15273     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15274                                         player->index_bit, dig_side);
15275
15276     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15277                                         player->index_bit, dig_side);
15278
15279     return MP_ACTION;
15280   }
15281   else
15282   {
15283     if (!PLAYER_SWITCHING(player, x, y))
15284     {
15285       player->is_switching = TRUE;
15286       player->switch_x = x;
15287       player->switch_y = y;
15288
15289       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15290                                  player->index_bit, dig_side);
15291       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15292                                           player->index_bit, dig_side);
15293
15294       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15295                                  player->index_bit, dig_side);
15296       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15297                                           player->index_bit, dig_side);
15298     }
15299
15300     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15301                                player->index_bit, dig_side);
15302     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15303                                         player->index_bit, dig_side);
15304
15305     return MP_NO_ACTION;
15306   }
15307
15308   player->push_delay = -1;
15309
15310   if (is_player)                /* function can also be called by EL_PENGUIN */
15311   {
15312     if (Feld[x][y] != element)          /* really digged/collected something */
15313     {
15314       player->is_collecting = !player->is_digging;
15315       player->is_active = TRUE;
15316     }
15317   }
15318
15319   return MP_MOVING;
15320 }
15321
15322 static boolean DigFieldByCE(int x, int y, int digging_element)
15323 {
15324   int element = Feld[x][y];
15325
15326   if (!IS_FREE(x, y))
15327   {
15328     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15329                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15330                   ACTION_BREAKING);
15331
15332     /* no element can dig solid indestructible elements */
15333     if (IS_INDESTRUCTIBLE(element) &&
15334         !IS_DIGGABLE(element) &&
15335         !IS_COLLECTIBLE(element))
15336       return FALSE;
15337
15338     if (AmoebaNr[x][y] &&
15339         (element == EL_AMOEBA_FULL ||
15340          element == EL_BD_AMOEBA ||
15341          element == EL_AMOEBA_GROWING))
15342     {
15343       AmoebaCnt[AmoebaNr[x][y]]--;
15344       AmoebaCnt2[AmoebaNr[x][y]]--;
15345     }
15346
15347     if (IS_MOVING(x, y))
15348       RemoveMovingField(x, y);
15349     else
15350     {
15351       RemoveField(x, y);
15352       TEST_DrawLevelField(x, y);
15353     }
15354
15355     /* if digged element was about to explode, prevent the explosion */
15356     ExplodeField[x][y] = EX_TYPE_NONE;
15357
15358     PlayLevelSoundAction(x, y, action);
15359   }
15360
15361   Store[x][y] = EL_EMPTY;
15362
15363 #if 1
15364   /* this makes it possible to leave the removed element again */
15365   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15366     Store[x][y] = element;
15367 #else
15368   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15369   {
15370     int move_leave_element = element_info[digging_element].move_leave_element;
15371
15372     /* this makes it possible to leave the removed element again */
15373     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15374                    element : move_leave_element);
15375   }
15376 #endif
15377
15378   return TRUE;
15379 }
15380
15381 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15382 {
15383   int jx = player->jx, jy = player->jy;
15384   int x = jx + dx, y = jy + dy;
15385   int snap_direction = (dx == -1 ? MV_LEFT  :
15386                         dx == +1 ? MV_RIGHT :
15387                         dy == -1 ? MV_UP    :
15388                         dy == +1 ? MV_DOWN  : MV_NONE);
15389   boolean can_continue_snapping = (level.continuous_snapping &&
15390                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15391
15392   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15393     return FALSE;
15394
15395   if (!player->active || !IN_LEV_FIELD(x, y))
15396     return FALSE;
15397
15398   if (dx && dy)
15399     return FALSE;
15400
15401   if (!dx && !dy)
15402   {
15403     if (player->MovPos == 0)
15404       player->is_pushing = FALSE;
15405
15406     player->is_snapping = FALSE;
15407
15408     if (player->MovPos == 0)
15409     {
15410       player->is_moving = FALSE;
15411       player->is_digging = FALSE;
15412       player->is_collecting = FALSE;
15413     }
15414
15415     return FALSE;
15416   }
15417
15418 #if USE_NEW_CONTINUOUS_SNAPPING
15419   /* prevent snapping with already pressed snap key when not allowed */
15420   if (player->is_snapping && !can_continue_snapping)
15421     return FALSE;
15422 #else
15423   if (player->is_snapping)
15424     return FALSE;
15425 #endif
15426
15427   player->MovDir = snap_direction;
15428
15429   if (player->MovPos == 0)
15430   {
15431     player->is_moving = FALSE;
15432     player->is_digging = FALSE;
15433     player->is_collecting = FALSE;
15434   }
15435
15436   player->is_dropping = FALSE;
15437   player->is_dropping_pressed = FALSE;
15438   player->drop_pressed_delay = 0;
15439
15440   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15441     return FALSE;
15442
15443   player->is_snapping = TRUE;
15444   player->is_active = TRUE;
15445
15446   if (player->MovPos == 0)
15447   {
15448     player->is_moving = FALSE;
15449     player->is_digging = FALSE;
15450     player->is_collecting = FALSE;
15451   }
15452
15453   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15454     TEST_DrawLevelField(player->last_jx, player->last_jy);
15455
15456   TEST_DrawLevelField(x, y);
15457
15458   return TRUE;
15459 }
15460
15461 static boolean DropElement(struct PlayerInfo *player)
15462 {
15463   int old_element, new_element;
15464   int dropx = player->jx, dropy = player->jy;
15465   int drop_direction = player->MovDir;
15466   int drop_side = drop_direction;
15467 #if 1
15468   int drop_element = get_next_dropped_element(player);
15469 #else
15470   int drop_element = (player->inventory_size > 0 ?
15471                       player->inventory_element[player->inventory_size - 1] :
15472                       player->inventory_infinite_element != EL_UNDEFINED ?
15473                       player->inventory_infinite_element :
15474                       player->dynabombs_left > 0 ?
15475                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15476                       EL_UNDEFINED);
15477 #endif
15478
15479   player->is_dropping_pressed = TRUE;
15480
15481   /* do not drop an element on top of another element; when holding drop key
15482      pressed without moving, dropped element must move away before the next
15483      element can be dropped (this is especially important if the next element
15484      is dynamite, which can be placed on background for historical reasons) */
15485   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15486     return MP_ACTION;
15487
15488   if (IS_THROWABLE(drop_element))
15489   {
15490     dropx += GET_DX_FROM_DIR(drop_direction);
15491     dropy += GET_DY_FROM_DIR(drop_direction);
15492
15493     if (!IN_LEV_FIELD(dropx, dropy))
15494       return FALSE;
15495   }
15496
15497   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15498   new_element = drop_element;           /* default: no change when dropping */
15499
15500   /* check if player is active, not moving and ready to drop */
15501   if (!player->active || player->MovPos || player->drop_delay > 0)
15502     return FALSE;
15503
15504   /* check if player has anything that can be dropped */
15505   if (new_element == EL_UNDEFINED)
15506     return FALSE;
15507
15508   /* check if drop key was pressed long enough for EM style dynamite */
15509   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15510     return FALSE;
15511
15512   /* check if anything can be dropped at the current position */
15513   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15514     return FALSE;
15515
15516   /* collected custom elements can only be dropped on empty fields */
15517   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15518     return FALSE;
15519
15520   if (old_element != EL_EMPTY)
15521     Back[dropx][dropy] = old_element;   /* store old element on this field */
15522
15523   ResetGfxAnimation(dropx, dropy);
15524   ResetRandomAnimationValue(dropx, dropy);
15525
15526   if (player->inventory_size > 0 ||
15527       player->inventory_infinite_element != EL_UNDEFINED)
15528   {
15529     if (player->inventory_size > 0)
15530     {
15531       player->inventory_size--;
15532
15533       DrawGameDoorValues();
15534
15535       if (new_element == EL_DYNAMITE)
15536         new_element = EL_DYNAMITE_ACTIVE;
15537       else if (new_element == EL_EM_DYNAMITE)
15538         new_element = EL_EM_DYNAMITE_ACTIVE;
15539       else if (new_element == EL_SP_DISK_RED)
15540         new_element = EL_SP_DISK_RED_ACTIVE;
15541     }
15542
15543     Feld[dropx][dropy] = new_element;
15544
15545     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15546       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15547                           el2img(Feld[dropx][dropy]), 0);
15548
15549     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15550
15551     /* needed if previous element just changed to "empty" in the last frame */
15552     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15553
15554     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15555                                player->index_bit, drop_side);
15556     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15557                                         CE_PLAYER_DROPS_X,
15558                                         player->index_bit, drop_side);
15559
15560     TestIfElementTouchesCustomElement(dropx, dropy);
15561   }
15562   else          /* player is dropping a dyna bomb */
15563   {
15564     player->dynabombs_left--;
15565
15566     Feld[dropx][dropy] = new_element;
15567
15568     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15569       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15570                           el2img(Feld[dropx][dropy]), 0);
15571
15572     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15573   }
15574
15575   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15576     InitField_WithBug1(dropx, dropy, FALSE);
15577
15578   new_element = Feld[dropx][dropy];     /* element might have changed */
15579
15580   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15581       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15582   {
15583     int move_direction, nextx, nexty;
15584
15585     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15586       MovDir[dropx][dropy] = drop_direction;
15587
15588     move_direction = MovDir[dropx][dropy];
15589     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15590     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15591
15592     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15593
15594 #if USE_FIX_IMPACT_COLLISION
15595     /* do not cause impact style collision by dropping elements that can fall */
15596     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15597 #else
15598     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15599 #endif
15600   }
15601
15602   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15603   player->is_dropping = TRUE;
15604
15605   player->drop_pressed_delay = 0;
15606   player->is_dropping_pressed = FALSE;
15607
15608   player->drop_x = dropx;
15609   player->drop_y = dropy;
15610
15611   return TRUE;
15612 }
15613
15614 /* ------------------------------------------------------------------------- */
15615 /* game sound playing functions                                              */
15616 /* ------------------------------------------------------------------------- */
15617
15618 static int *loop_sound_frame = NULL;
15619 static int *loop_sound_volume = NULL;
15620
15621 void InitPlayLevelSound()
15622 {
15623   int num_sounds = getSoundListSize();
15624
15625   checked_free(loop_sound_frame);
15626   checked_free(loop_sound_volume);
15627
15628   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15629   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15630 }
15631
15632 static void PlayLevelSound(int x, int y, int nr)
15633 {
15634   int sx = SCREENX(x), sy = SCREENY(y);
15635   int volume, stereo_position;
15636   int max_distance = 8;
15637   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15638
15639   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15640       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15641     return;
15642
15643   if (!IN_LEV_FIELD(x, y) ||
15644       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15645       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15646     return;
15647
15648   volume = SOUND_MAX_VOLUME;
15649
15650   if (!IN_SCR_FIELD(sx, sy))
15651   {
15652     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15653     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15654
15655     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15656   }
15657
15658   stereo_position = (SOUND_MAX_LEFT +
15659                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15660                      (SCR_FIELDX + 2 * max_distance));
15661
15662   if (IS_LOOP_SOUND(nr))
15663   {
15664     /* This assures that quieter loop sounds do not overwrite louder ones,
15665        while restarting sound volume comparison with each new game frame. */
15666
15667     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15668       return;
15669
15670     loop_sound_volume[nr] = volume;
15671     loop_sound_frame[nr] = FrameCounter;
15672   }
15673
15674   PlaySoundExt(nr, volume, stereo_position, type);
15675 }
15676
15677 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15678 {
15679   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15680                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15681                  y < LEVELY(BY1) ? LEVELY(BY1) :
15682                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15683                  sound_action);
15684 }
15685
15686 static void PlayLevelSoundAction(int x, int y, int action)
15687 {
15688   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15689 }
15690
15691 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15692 {
15693   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15694
15695   if (sound_effect != SND_UNDEFINED)
15696     PlayLevelSound(x, y, sound_effect);
15697 }
15698
15699 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15700                                               int action)
15701 {
15702   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15703
15704   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15705     PlayLevelSound(x, y, sound_effect);
15706 }
15707
15708 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15709 {
15710   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15711
15712   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15713     PlayLevelSound(x, y, sound_effect);
15714 }
15715
15716 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15717 {
15718   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15719
15720   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15721     StopSound(sound_effect);
15722 }
15723
15724 static void PlayLevelMusic()
15725 {
15726   if (levelset.music[level_nr] != MUS_UNDEFINED)
15727     PlayMusic(levelset.music[level_nr]);        /* from config file */
15728   else
15729     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15730 }
15731
15732 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15733 {
15734   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15735   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15736   int x = xx - 1 - offset;
15737   int y = yy - 1 - offset;
15738
15739   switch (sample)
15740   {
15741     case SAMPLE_blank:
15742       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15743       break;
15744
15745     case SAMPLE_roll:
15746       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15747       break;
15748
15749     case SAMPLE_stone:
15750       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15751       break;
15752
15753     case SAMPLE_nut:
15754       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15755       break;
15756
15757     case SAMPLE_crack:
15758       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15759       break;
15760
15761     case SAMPLE_bug:
15762       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15763       break;
15764
15765     case SAMPLE_tank:
15766       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15767       break;
15768
15769     case SAMPLE_android_clone:
15770       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15771       break;
15772
15773     case SAMPLE_android_move:
15774       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15775       break;
15776
15777     case SAMPLE_spring:
15778       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15779       break;
15780
15781     case SAMPLE_slurp:
15782       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15783       break;
15784
15785     case SAMPLE_eater:
15786       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15787       break;
15788
15789     case SAMPLE_eater_eat:
15790       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15791       break;
15792
15793     case SAMPLE_alien:
15794       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15795       break;
15796
15797     case SAMPLE_collect:
15798       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15799       break;
15800
15801     case SAMPLE_diamond:
15802       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15803       break;
15804
15805     case SAMPLE_squash:
15806       /* !!! CHECK THIS !!! */
15807 #if 1
15808       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15809 #else
15810       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15811 #endif
15812       break;
15813
15814     case SAMPLE_wonderfall:
15815       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15816       break;
15817
15818     case SAMPLE_drip:
15819       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15820       break;
15821
15822     case SAMPLE_push:
15823       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15824       break;
15825
15826     case SAMPLE_dirt:
15827       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15828       break;
15829
15830     case SAMPLE_acid:
15831       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15832       break;
15833
15834     case SAMPLE_ball:
15835       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15836       break;
15837
15838     case SAMPLE_grow:
15839       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15840       break;
15841
15842     case SAMPLE_wonder:
15843       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15844       break;
15845
15846     case SAMPLE_door:
15847       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15848       break;
15849
15850     case SAMPLE_exit_open:
15851       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15852       break;
15853
15854     case SAMPLE_exit_leave:
15855       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15856       break;
15857
15858     case SAMPLE_dynamite:
15859       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15860       break;
15861
15862     case SAMPLE_tick:
15863       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15864       break;
15865
15866     case SAMPLE_press:
15867       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15868       break;
15869
15870     case SAMPLE_wheel:
15871       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15872       break;
15873
15874     case SAMPLE_boom:
15875       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15876       break;
15877
15878     case SAMPLE_die:
15879       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15880       break;
15881
15882     case SAMPLE_time:
15883       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15884       break;
15885
15886     default:
15887       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15888       break;
15889   }
15890 }
15891
15892 #if 0
15893 void ChangeTime(int value)
15894 {
15895   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15896
15897   *time += value;
15898
15899   /* EMC game engine uses value from time counter of RND game engine */
15900   level.native_em_level->lev->time = *time;
15901
15902   DrawGameValue_Time(*time);
15903 }
15904
15905 void RaiseScore(int value)
15906 {
15907   /* EMC game engine and RND game engine have separate score counters */
15908   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15909                 &level.native_em_level->lev->score : &local_player->score);
15910
15911   *score += value;
15912
15913   DrawGameValue_Score(*score);
15914 }
15915 #endif
15916
15917 void RaiseScore(int value)
15918 {
15919   local_player->score += value;
15920
15921 #if 1
15922   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15923
15924   DisplayGameControlValues();
15925 #else
15926   DrawGameValue_Score(local_player->score);
15927 #endif
15928 }
15929
15930 void RaiseScoreElement(int element)
15931 {
15932   switch (element)
15933   {
15934     case EL_EMERALD:
15935     case EL_BD_DIAMOND:
15936     case EL_EMERALD_YELLOW:
15937     case EL_EMERALD_RED:
15938     case EL_EMERALD_PURPLE:
15939     case EL_SP_INFOTRON:
15940       RaiseScore(level.score[SC_EMERALD]);
15941       break;
15942     case EL_DIAMOND:
15943       RaiseScore(level.score[SC_DIAMOND]);
15944       break;
15945     case EL_CRYSTAL:
15946       RaiseScore(level.score[SC_CRYSTAL]);
15947       break;
15948     case EL_PEARL:
15949       RaiseScore(level.score[SC_PEARL]);
15950       break;
15951     case EL_BUG:
15952     case EL_BD_BUTTERFLY:
15953     case EL_SP_ELECTRON:
15954       RaiseScore(level.score[SC_BUG]);
15955       break;
15956     case EL_SPACESHIP:
15957     case EL_BD_FIREFLY:
15958     case EL_SP_SNIKSNAK:
15959       RaiseScore(level.score[SC_SPACESHIP]);
15960       break;
15961     case EL_YAMYAM:
15962     case EL_DARK_YAMYAM:
15963       RaiseScore(level.score[SC_YAMYAM]);
15964       break;
15965     case EL_ROBOT:
15966       RaiseScore(level.score[SC_ROBOT]);
15967       break;
15968     case EL_PACMAN:
15969       RaiseScore(level.score[SC_PACMAN]);
15970       break;
15971     case EL_NUT:
15972       RaiseScore(level.score[SC_NUT]);
15973       break;
15974     case EL_DYNAMITE:
15975     case EL_EM_DYNAMITE:
15976     case EL_SP_DISK_RED:
15977     case EL_DYNABOMB_INCREASE_NUMBER:
15978     case EL_DYNABOMB_INCREASE_SIZE:
15979     case EL_DYNABOMB_INCREASE_POWER:
15980       RaiseScore(level.score[SC_DYNAMITE]);
15981       break;
15982     case EL_SHIELD_NORMAL:
15983     case EL_SHIELD_DEADLY:
15984       RaiseScore(level.score[SC_SHIELD]);
15985       break;
15986     case EL_EXTRA_TIME:
15987       RaiseScore(level.extra_time_score);
15988       break;
15989     case EL_KEY_1:
15990     case EL_KEY_2:
15991     case EL_KEY_3:
15992     case EL_KEY_4:
15993     case EL_EM_KEY_1:
15994     case EL_EM_KEY_2:
15995     case EL_EM_KEY_3:
15996     case EL_EM_KEY_4:
15997     case EL_EMC_KEY_5:
15998     case EL_EMC_KEY_6:
15999     case EL_EMC_KEY_7:
16000     case EL_EMC_KEY_8:
16001     case EL_DC_KEY_WHITE:
16002       RaiseScore(level.score[SC_KEY]);
16003       break;
16004     default:
16005       RaiseScore(element_info[element].collect_score);
16006       break;
16007   }
16008 }
16009
16010 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16011 {
16012   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16013   {
16014 #if defined(NETWORK_AVALIABLE)
16015     if (options.network)
16016       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16017     else
16018 #endif
16019     {
16020       if (quick_quit)
16021       {
16022 #if 1
16023
16024 #if 1
16025         FadeSkipNextFadeIn();
16026 #else
16027         fading = fading_none;
16028 #endif
16029
16030 #else
16031         OpenDoor(DOOR_CLOSE_1);
16032 #endif
16033
16034         game_status = GAME_MODE_MAIN;
16035
16036 #if 1
16037         DrawAndFadeInMainMenu(REDRAW_FIELD);
16038 #else
16039         DrawMainMenu();
16040 #endif
16041       }
16042       else
16043       {
16044 #if 0
16045         FadeOut(REDRAW_FIELD);
16046 #endif
16047
16048         game_status = GAME_MODE_MAIN;
16049
16050         DrawAndFadeInMainMenu(REDRAW_FIELD);
16051       }
16052     }
16053   }
16054   else          /* continue playing the game */
16055   {
16056     if (tape.playing && tape.deactivate_display)
16057       TapeDeactivateDisplayOff(TRUE);
16058
16059     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16060
16061     if (tape.playing && tape.deactivate_display)
16062       TapeDeactivateDisplayOn();
16063   }
16064 }
16065
16066 void RequestQuitGame(boolean ask_if_really_quit)
16067 {
16068   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16069   boolean skip_request = AllPlayersGone || quick_quit;
16070
16071   RequestQuitGameExt(skip_request, quick_quit,
16072                      "Do you really want to quit the game ?");
16073 }
16074
16075
16076 /* ------------------------------------------------------------------------- */
16077 /* random generator functions                                                */
16078 /* ------------------------------------------------------------------------- */
16079
16080 unsigned int InitEngineRandom_RND(long seed)
16081 {
16082   game.num_random_calls = 0;
16083
16084 #if 0
16085   unsigned int rnd_seed = InitEngineRandom(seed);
16086
16087   printf("::: START RND: %d\n", rnd_seed);
16088
16089   return rnd_seed;
16090 #else
16091
16092   return InitEngineRandom(seed);
16093
16094 #endif
16095
16096 }
16097
16098 unsigned int RND(int max)
16099 {
16100   if (max > 0)
16101   {
16102     game.num_random_calls++;
16103
16104     return GetEngineRandom(max);
16105   }
16106
16107   return 0;
16108 }
16109
16110
16111 /* ------------------------------------------------------------------------- */
16112 /* game engine snapshot handling functions                                   */
16113 /* ------------------------------------------------------------------------- */
16114
16115 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16116
16117 struct EngineSnapshotInfo
16118 {
16119   /* runtime values for custom element collect score */
16120   int collect_score[NUM_CUSTOM_ELEMENTS];
16121
16122   /* runtime values for group element choice position */
16123   int choice_pos[NUM_GROUP_ELEMENTS];
16124
16125   /* runtime values for belt position animations */
16126   int belt_graphic[4 * NUM_BELT_PARTS];
16127   int belt_anim_mode[4 * NUM_BELT_PARTS];
16128 };
16129
16130 struct EngineSnapshotNodeInfo
16131 {
16132   void *buffer_orig;
16133   void *buffer_copy;
16134   int size;
16135 };
16136
16137 static struct EngineSnapshotInfo engine_snapshot_rnd;
16138 static ListNode *engine_snapshot_list = NULL;
16139 static char *snapshot_level_identifier = NULL;
16140 static int snapshot_level_nr = -1;
16141
16142 void FreeEngineSnapshot()
16143 {
16144   while (engine_snapshot_list != NULL)
16145     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16146                        checked_free);
16147
16148   setString(&snapshot_level_identifier, NULL);
16149   snapshot_level_nr = -1;
16150 }
16151
16152 static void SaveEngineSnapshotValues_RND()
16153 {
16154   static int belt_base_active_element[4] =
16155   {
16156     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16157     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16158     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16159     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16160   };
16161   int i, j;
16162
16163   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16164   {
16165     int element = EL_CUSTOM_START + i;
16166
16167     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16168   }
16169
16170   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16171   {
16172     int element = EL_GROUP_START + i;
16173
16174     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16175   }
16176
16177   for (i = 0; i < 4; i++)
16178   {
16179     for (j = 0; j < NUM_BELT_PARTS; j++)
16180     {
16181       int element = belt_base_active_element[i] + j;
16182       int graphic = el2img(element);
16183       int anim_mode = graphic_info[graphic].anim_mode;
16184
16185       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16186       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16187     }
16188   }
16189 }
16190
16191 static void LoadEngineSnapshotValues_RND()
16192 {
16193   unsigned long num_random_calls = game.num_random_calls;
16194   int i, j;
16195
16196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16197   {
16198     int element = EL_CUSTOM_START + i;
16199
16200     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16201   }
16202
16203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16204   {
16205     int element = EL_GROUP_START + i;
16206
16207     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16208   }
16209
16210   for (i = 0; i < 4; i++)
16211   {
16212     for (j = 0; j < NUM_BELT_PARTS; j++)
16213     {
16214       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16215       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16216
16217       graphic_info[graphic].anim_mode = anim_mode;
16218     }
16219   }
16220
16221   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16222   {
16223     InitRND(tape.random_seed);
16224     for (i = 0; i < num_random_calls; i++)
16225       RND(1);
16226   }
16227
16228   if (game.num_random_calls != num_random_calls)
16229   {
16230     Error(ERR_INFO, "number of random calls out of sync");
16231     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16232     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16233     Error(ERR_EXIT, "this should not happen -- please debug");
16234   }
16235 }
16236
16237 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16238 {
16239   struct EngineSnapshotNodeInfo *bi =
16240     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16241
16242   bi->buffer_orig = buffer;
16243   bi->buffer_copy = checked_malloc(size);
16244   bi->size = size;
16245
16246   memcpy(bi->buffer_copy, buffer, size);
16247
16248   addNodeToList(&engine_snapshot_list, NULL, bi);
16249 }
16250
16251 void SaveEngineSnapshot()
16252 {
16253   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16254
16255   if (level_editor_test_game)   /* do not save snapshots from editor */
16256     return;
16257
16258   /* copy some special values to a structure better suited for the snapshot */
16259
16260   SaveEngineSnapshotValues_RND();
16261   SaveEngineSnapshotValues_EM();
16262
16263   /* save values stored in special snapshot structure */
16264
16265   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16266   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16267
16268   /* save further RND engine values */
16269
16270   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16271   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16272   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16273
16274   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16275   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16276   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16277   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16278
16279   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16280   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16281   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16282   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16283   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16284
16285   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16286   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16287   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16288
16289   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16290
16291   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16292
16293   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16294   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16295
16296   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16297   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16298   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16299   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16300   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16301   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16302   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16303   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16304   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16305   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16306   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16307   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16308   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16309   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16310   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16311   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16312   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16313   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16314
16315   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16316   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16317
16318   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16319   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16320   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16321
16322   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16323   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16324
16325   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16326   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16327   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16328   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16329   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16330
16331   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16332   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16333
16334   /* save level identification information */
16335
16336   setString(&snapshot_level_identifier, leveldir_current->identifier);
16337   snapshot_level_nr = level_nr;
16338
16339 #if 0
16340   ListNode *node = engine_snapshot_list;
16341   int num_bytes = 0;
16342
16343   while (node != NULL)
16344   {
16345     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16346
16347     node = node->next;
16348   }
16349
16350   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16351 #endif
16352 }
16353
16354 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16355 {
16356   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16357 }
16358
16359 void LoadEngineSnapshot()
16360 {
16361   ListNode *node = engine_snapshot_list;
16362
16363   if (engine_snapshot_list == NULL)
16364     return;
16365
16366   while (node != NULL)
16367   {
16368     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16369
16370     node = node->next;
16371   }
16372
16373   /* restore special values from snapshot structure */
16374
16375   LoadEngineSnapshotValues_RND();
16376   LoadEngineSnapshotValues_EM();
16377 }
16378
16379 boolean CheckEngineSnapshot()
16380 {
16381   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16382           snapshot_level_nr == level_nr);
16383 }
16384
16385
16386 /* ---------- new game button stuff ---------------------------------------- */
16387
16388 /* graphic position values for game buttons */
16389 #define GAME_BUTTON_XSIZE       30
16390 #define GAME_BUTTON_YSIZE       30
16391 #define GAME_BUTTON_XPOS        5
16392 #define GAME_BUTTON_YPOS        215
16393 #define SOUND_BUTTON_XPOS       5
16394 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16395
16396 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16397 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16398 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16399 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16400 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16401 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16402
16403 static struct
16404 {
16405   int *x, *y;
16406   int gd_x, gd_y;
16407   int gadget_id;
16408   char *infotext;
16409 } gamebutton_info[NUM_GAME_BUTTONS] =
16410 {
16411 #if 1
16412   {
16413     &game.button.stop.x,        &game.button.stop.y,
16414     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16415     GAME_CTRL_ID_STOP,
16416     "stop game"
16417   },
16418   {
16419     &game.button.pause.x,       &game.button.pause.y,
16420     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16421     GAME_CTRL_ID_PAUSE,
16422     "pause game"
16423   },
16424   {
16425     &game.button.play.x,        &game.button.play.y,
16426     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16427     GAME_CTRL_ID_PLAY,
16428     "play game"
16429   },
16430   {
16431     &game.button.sound_music.x, &game.button.sound_music.y,
16432     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16433     SOUND_CTRL_ID_MUSIC,
16434     "background music on/off"
16435   },
16436   {
16437     &game.button.sound_loops.x, &game.button.sound_loops.y,
16438     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16439     SOUND_CTRL_ID_LOOPS,
16440     "sound loops on/off"
16441   },
16442   {
16443     &game.button.sound_simple.x,&game.button.sound_simple.y,
16444     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16445     SOUND_CTRL_ID_SIMPLE,
16446     "normal sounds on/off"
16447   }
16448 #else
16449   {
16450     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16451     GAME_CTRL_ID_STOP,
16452     "stop game"
16453   },
16454   {
16455     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16456     GAME_CTRL_ID_PAUSE,
16457     "pause game"
16458   },
16459   {
16460     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16461     GAME_CTRL_ID_PLAY,
16462     "play game"
16463   },
16464   {
16465     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16466     SOUND_CTRL_ID_MUSIC,
16467     "background music on/off"
16468   },
16469   {
16470     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16471     SOUND_CTRL_ID_LOOPS,
16472     "sound loops on/off"
16473   },
16474   {
16475     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16476     SOUND_CTRL_ID_SIMPLE,
16477     "normal sounds on/off"
16478   }
16479 #endif
16480 };
16481
16482 void CreateGameButtons()
16483 {
16484   int i;
16485
16486   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16487   {
16488     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16489     struct GadgetInfo *gi;
16490     int button_type;
16491     boolean checked;
16492     unsigned long event_mask;
16493     int x, y;
16494     int gd_xoffset, gd_yoffset;
16495     int gd_x1, gd_x2, gd_y1, gd_y2;
16496     int id = i;
16497
16498     x = DX + *gamebutton_info[i].x;
16499     y = DY + *gamebutton_info[i].y;
16500     gd_xoffset = gamebutton_info[i].gd_x;
16501     gd_yoffset = gamebutton_info[i].gd_y;
16502     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16503     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16504
16505     if (id == GAME_CTRL_ID_STOP ||
16506         id == GAME_CTRL_ID_PAUSE ||
16507         id == GAME_CTRL_ID_PLAY)
16508     {
16509       button_type = GD_TYPE_NORMAL_BUTTON;
16510       checked = FALSE;
16511       event_mask = GD_EVENT_RELEASED;
16512       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16513       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16514     }
16515     else
16516     {
16517       button_type = GD_TYPE_CHECK_BUTTON;
16518       checked =
16519         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16520          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16521          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16522       event_mask = GD_EVENT_PRESSED;
16523       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16524       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16525     }
16526
16527     gi = CreateGadget(GDI_CUSTOM_ID, id,
16528                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16529 #if 1
16530                       GDI_X, x,
16531                       GDI_Y, y,
16532 #else
16533                       GDI_X, DX + gd_xoffset,
16534                       GDI_Y, DY + gd_yoffset,
16535 #endif
16536                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16537                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16538                       GDI_TYPE, button_type,
16539                       GDI_STATE, GD_BUTTON_UNPRESSED,
16540                       GDI_CHECKED, checked,
16541                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16542                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16543                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16544                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16545                       GDI_DIRECT_DRAW, FALSE,
16546                       GDI_EVENT_MASK, event_mask,
16547                       GDI_CALLBACK_ACTION, HandleGameButtons,
16548                       GDI_END);
16549
16550     if (gi == NULL)
16551       Error(ERR_EXIT, "cannot create gadget");
16552
16553     game_gadget[id] = gi;
16554   }
16555 }
16556
16557 void FreeGameButtons()
16558 {
16559   int i;
16560
16561   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16562     FreeGadget(game_gadget[i]);
16563 }
16564
16565 static void MapGameButtons()
16566 {
16567   int i;
16568
16569   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16570     MapGadget(game_gadget[i]);
16571 }
16572
16573 void UnmapGameButtons()
16574 {
16575   int i;
16576
16577   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16578     UnmapGadget(game_gadget[i]);
16579 }
16580
16581 void RedrawGameButtons()
16582 {
16583   int i;
16584
16585   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16586     RedrawGadget(game_gadget[i]);
16587 }
16588
16589 static void HandleGameButtons(struct GadgetInfo *gi)
16590 {
16591   int id = gi->custom_id;
16592
16593   if (game_status != GAME_MODE_PLAYING)
16594     return;
16595
16596   switch (id)
16597   {
16598     case GAME_CTRL_ID_STOP:
16599       if (tape.playing)
16600         TapeStop();
16601       else
16602         RequestQuitGame(TRUE);
16603       break;
16604
16605     case GAME_CTRL_ID_PAUSE:
16606       if (options.network)
16607       {
16608 #if defined(NETWORK_AVALIABLE)
16609         if (tape.pausing)
16610           SendToServer_ContinuePlaying();
16611         else
16612           SendToServer_PausePlaying();
16613 #endif
16614       }
16615       else
16616         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16617       break;
16618
16619     case GAME_CTRL_ID_PLAY:
16620       if (tape.pausing)
16621       {
16622 #if defined(NETWORK_AVALIABLE)
16623         if (options.network)
16624           SendToServer_ContinuePlaying();
16625         else
16626 #endif
16627         {
16628           tape.pausing = FALSE;
16629           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16630         }
16631       }
16632       break;
16633
16634     case SOUND_CTRL_ID_MUSIC:
16635       if (setup.sound_music)
16636       { 
16637         setup.sound_music = FALSE;
16638         FadeMusic();
16639       }
16640       else if (audio.music_available)
16641       { 
16642         setup.sound = setup.sound_music = TRUE;
16643
16644         SetAudioMode(setup.sound);
16645
16646         PlayLevelMusic();
16647       }
16648       break;
16649
16650     case SOUND_CTRL_ID_LOOPS:
16651       if (setup.sound_loops)
16652         setup.sound_loops = FALSE;
16653       else if (audio.loops_available)
16654       {
16655         setup.sound = setup.sound_loops = TRUE;
16656         SetAudioMode(setup.sound);
16657       }
16658       break;
16659
16660     case SOUND_CTRL_ID_SIMPLE:
16661       if (setup.sound_simple)
16662         setup.sound_simple = FALSE;
16663       else if (audio.sound_available)
16664       {
16665         setup.sound = setup.sound_simple = TRUE;
16666         SetAudioMode(setup.sound);
16667       }
16668       break;
16669
16670     default:
16671       break;
16672   }
16673 }