rnd-20080802-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     case CA_SET_LEVEL_RANDOM_SEED:
10434     {
10435 #if 1
10436       /* ensure that setting a new random seed while playing is predictable */
10437       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10438 #else
10439       InitRND(action_arg_number_new);
10440 #endif
10441
10442 #if 0
10443       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10444 #endif
10445
10446 #if 0
10447       {
10448         int i;
10449
10450         printf("::: ");
10451         for (i = 0; i < 9; i++)
10452           printf("%d, ", RND(2));
10453         printf("\n");
10454       }
10455 #endif
10456
10457       break;
10458     }
10459
10460     /* ---------- player actions  ------------------------------------------ */
10461
10462     case CA_MOVE_PLAYER:
10463     {
10464       /* automatically move to the next field in specified direction */
10465       for (i = 0; i < MAX_PLAYERS; i++)
10466         if (trigger_player_bits & (1 << i))
10467           stored_player[i].programmed_action = action_arg_direction;
10468
10469       break;
10470     }
10471
10472     case CA_EXIT_PLAYER:
10473     {
10474       for (i = 0; i < MAX_PLAYERS; i++)
10475         if (action_arg_player_bits & (1 << i))
10476           PlayerWins(&stored_player[i]);
10477
10478       break;
10479     }
10480
10481     case CA_KILL_PLAYER:
10482     {
10483       for (i = 0; i < MAX_PLAYERS; i++)
10484         if (action_arg_player_bits & (1 << i))
10485           KillPlayer(&stored_player[i]);
10486
10487       break;
10488     }
10489
10490     case CA_SET_PLAYER_KEYS:
10491     {
10492       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10493       int element = getSpecialActionElement(action_arg_element,
10494                                             action_arg_number, EL_KEY_1);
10495
10496       if (IS_KEY(element))
10497       {
10498         for (i = 0; i < MAX_PLAYERS; i++)
10499         {
10500           if (trigger_player_bits & (1 << i))
10501           {
10502             stored_player[i].key[KEY_NR(element)] = key_state;
10503
10504             DrawGameDoorValues();
10505           }
10506         }
10507       }
10508
10509       break;
10510     }
10511
10512     case CA_SET_PLAYER_SPEED:
10513     {
10514 #if 0
10515       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10516 #endif
10517
10518       for (i = 0; i < MAX_PLAYERS; i++)
10519       {
10520         if (trigger_player_bits & (1 << i))
10521         {
10522           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10523
10524           if (action_arg == CA_ARG_SPEED_FASTER &&
10525               stored_player[i].cannot_move)
10526           {
10527             action_arg_number = STEPSIZE_VERY_SLOW;
10528           }
10529           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10530                    action_arg == CA_ARG_SPEED_FASTER)
10531           {
10532             action_arg_number = 2;
10533             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10534                            CA_MODE_MULTIPLY);
10535           }
10536           else if (action_arg == CA_ARG_NUMBER_RESET)
10537           {
10538             action_arg_number = level.initial_player_stepsize[i];
10539           }
10540
10541           move_stepsize =
10542             getModifiedActionNumber(move_stepsize,
10543                                     action_mode,
10544                                     action_arg_number,
10545                                     action_arg_number_min,
10546                                     action_arg_number_max);
10547
10548           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10549         }
10550       }
10551
10552       break;
10553     }
10554
10555     case CA_SET_PLAYER_SHIELD:
10556     {
10557       for (i = 0; i < MAX_PLAYERS; i++)
10558       {
10559         if (trigger_player_bits & (1 << i))
10560         {
10561           if (action_arg == CA_ARG_SHIELD_OFF)
10562           {
10563             stored_player[i].shield_normal_time_left = 0;
10564             stored_player[i].shield_deadly_time_left = 0;
10565           }
10566           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10567           {
10568             stored_player[i].shield_normal_time_left = 999999;
10569           }
10570           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10571           {
10572             stored_player[i].shield_normal_time_left = 999999;
10573             stored_player[i].shield_deadly_time_left = 999999;
10574           }
10575         }
10576       }
10577
10578       break;
10579     }
10580
10581 #if USE_PLAYER_GRAVITY
10582     case CA_SET_PLAYER_GRAVITY:
10583     {
10584       for (i = 0; i < MAX_PLAYERS; i++)
10585       {
10586         if (trigger_player_bits & (1 << i))
10587         {
10588           stored_player[i].gravity =
10589             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10590              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10591              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10592              stored_player[i].gravity);
10593         }
10594       }
10595
10596       break;
10597     }
10598 #endif
10599
10600     case CA_SET_PLAYER_ARTWORK:
10601     {
10602       for (i = 0; i < MAX_PLAYERS; i++)
10603       {
10604         if (trigger_player_bits & (1 << i))
10605         {
10606           int artwork_element = action_arg_element;
10607
10608           if (action_arg == CA_ARG_ELEMENT_RESET)
10609             artwork_element =
10610               (level.use_artwork_element[i] ? level.artwork_element[i] :
10611                stored_player[i].element_nr);
10612
10613 #if USE_GFX_RESET_PLAYER_ARTWORK
10614           if (stored_player[i].artwork_element != artwork_element)
10615             stored_player[i].Frame = 0;
10616 #endif
10617
10618           stored_player[i].artwork_element = artwork_element;
10619
10620           SetPlayerWaiting(&stored_player[i], FALSE);
10621
10622           /* set number of special actions for bored and sleeping animation */
10623           stored_player[i].num_special_action_bored =
10624             get_num_special_action(artwork_element,
10625                                    ACTION_BORING_1, ACTION_BORING_LAST);
10626           stored_player[i].num_special_action_sleeping =
10627             get_num_special_action(artwork_element,
10628                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10629         }
10630       }
10631
10632       break;
10633     }
10634
10635     case CA_SET_PLAYER_INVENTORY:
10636     {
10637       for (i = 0; i < MAX_PLAYERS; i++)
10638       {
10639         struct PlayerInfo *player = &stored_player[i];
10640         int j, k;
10641
10642         if (trigger_player_bits & (1 << i))
10643         {
10644           int inventory_element = action_arg_element;
10645
10646           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10647               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10648               action_arg == CA_ARG_ELEMENT_ACTION)
10649           {
10650             int element = inventory_element;
10651             int collect_count = element_info[element].collect_count_initial;
10652
10653             if (!IS_CUSTOM_ELEMENT(element))
10654               collect_count = 1;
10655
10656             if (collect_count == 0)
10657               player->inventory_infinite_element = element;
10658             else
10659               for (k = 0; k < collect_count; k++)
10660                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10661                   player->inventory_element[player->inventory_size++] =
10662                     element;
10663           }
10664           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10665                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10666                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10667           {
10668             if (player->inventory_infinite_element != EL_UNDEFINED &&
10669                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10670                                      action_arg_element_raw))
10671               player->inventory_infinite_element = EL_UNDEFINED;
10672
10673             for (k = 0, j = 0; j < player->inventory_size; j++)
10674             {
10675               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10676                                         action_arg_element_raw))
10677                 player->inventory_element[k++] = player->inventory_element[j];
10678             }
10679
10680             player->inventory_size = k;
10681           }
10682           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10683           {
10684             if (player->inventory_size > 0)
10685             {
10686               for (j = 0; j < player->inventory_size - 1; j++)
10687                 player->inventory_element[j] = player->inventory_element[j + 1];
10688
10689               player->inventory_size--;
10690             }
10691           }
10692           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10693           {
10694             if (player->inventory_size > 0)
10695               player->inventory_size--;
10696           }
10697           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10698           {
10699             player->inventory_infinite_element = EL_UNDEFINED;
10700             player->inventory_size = 0;
10701           }
10702           else if (action_arg == CA_ARG_INVENTORY_RESET)
10703           {
10704             player->inventory_infinite_element = EL_UNDEFINED;
10705             player->inventory_size = 0;
10706
10707             if (level.use_initial_inventory[i])
10708             {
10709               for (j = 0; j < level.initial_inventory_size[i]; j++)
10710               {
10711                 int element = level.initial_inventory_content[i][j];
10712                 int collect_count = element_info[element].collect_count_initial;
10713
10714                 if (!IS_CUSTOM_ELEMENT(element))
10715                   collect_count = 1;
10716
10717                 if (collect_count == 0)
10718                   player->inventory_infinite_element = element;
10719                 else
10720                   for (k = 0; k < collect_count; k++)
10721                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10722                       player->inventory_element[player->inventory_size++] =
10723                         element;
10724               }
10725             }
10726           }
10727         }
10728       }
10729
10730       break;
10731     }
10732
10733     /* ---------- CE actions  ---------------------------------------------- */
10734
10735     case CA_SET_CE_VALUE:
10736     {
10737 #if USE_NEW_CUSTOM_VALUE
10738       int last_ce_value = CustomValue[x][y];
10739
10740       CustomValue[x][y] = action_arg_number_new;
10741
10742       if (CustomValue[x][y] != last_ce_value)
10743       {
10744         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10745         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10746
10747         if (CustomValue[x][y] == 0)
10748         {
10749           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10750           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10751         }
10752       }
10753 #endif
10754
10755       break;
10756     }
10757
10758     case CA_SET_CE_SCORE:
10759     {
10760 #if USE_NEW_CUSTOM_VALUE
10761       int last_ce_score = ei->collect_score;
10762
10763       ei->collect_score = action_arg_number_new;
10764
10765       if (ei->collect_score != last_ce_score)
10766       {
10767         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10768         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10769
10770         if (ei->collect_score == 0)
10771         {
10772           int xx, yy;
10773
10774           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10775           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10776
10777           /*
10778             This is a very special case that seems to be a mixture between
10779             CheckElementChange() and CheckTriggeredElementChange(): while
10780             the first one only affects single elements that are triggered
10781             directly, the second one affects multiple elements in the playfield
10782             that are triggered indirectly by another element. This is a third
10783             case: Changing the CE score always affects multiple identical CEs,
10784             so every affected CE must be checked, not only the single CE for
10785             which the CE score was changed in the first place (as every instance
10786             of that CE shares the same CE score, and therefore also can change)!
10787           */
10788           SCAN_PLAYFIELD(xx, yy)
10789           {
10790             if (Feld[xx][yy] == element)
10791               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10792                                  CE_SCORE_GETS_ZERO);
10793           }
10794         }
10795       }
10796 #endif
10797
10798       break;
10799     }
10800
10801     case CA_SET_CE_ARTWORK:
10802     {
10803       int artwork_element = action_arg_element;
10804       boolean reset_frame = FALSE;
10805       int xx, yy;
10806
10807       if (action_arg == CA_ARG_ELEMENT_RESET)
10808         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10809                            element);
10810
10811       if (ei->gfx_element != artwork_element)
10812         reset_frame = TRUE;
10813
10814       ei->gfx_element = artwork_element;
10815
10816       SCAN_PLAYFIELD(xx, yy)
10817       {
10818         if (Feld[xx][yy] == element)
10819         {
10820           if (reset_frame)
10821           {
10822             ResetGfxAnimation(xx, yy);
10823             ResetRandomAnimationValue(xx, yy);
10824           }
10825
10826           TEST_DrawLevelField(xx, yy);
10827         }
10828       }
10829
10830       break;
10831     }
10832
10833     /* ---------- engine actions  ------------------------------------------ */
10834
10835     case CA_SET_ENGINE_SCAN_MODE:
10836     {
10837       InitPlayfieldScanMode(action_arg);
10838
10839       break;
10840     }
10841
10842     default:
10843       break;
10844   }
10845 }
10846
10847 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10848 {
10849   int old_element = Feld[x][y];
10850   int new_element = GetElementFromGroupElement(element);
10851   int previous_move_direction = MovDir[x][y];
10852 #if USE_NEW_CUSTOM_VALUE
10853   int last_ce_value = CustomValue[x][y];
10854 #endif
10855   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10856   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10857   boolean add_player_onto_element = (new_element_is_player &&
10858 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10859                                      /* this breaks SnakeBite when a snake is
10860                                         halfway through a door that closes */
10861                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10862                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10863 #endif
10864                                      IS_WALKABLE(old_element));
10865
10866 #if 0
10867   /* check if element under the player changes from accessible to unaccessible
10868      (needed for special case of dropping element which then changes) */
10869   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10870       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10871   {
10872     Bang(x, y);
10873
10874     return;
10875   }
10876 #endif
10877
10878   if (!add_player_onto_element)
10879   {
10880     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10881       RemoveMovingField(x, y);
10882     else
10883       RemoveField(x, y);
10884
10885     Feld[x][y] = new_element;
10886
10887 #if !USE_GFX_RESET_GFX_ANIMATION
10888     ResetGfxAnimation(x, y);
10889     ResetRandomAnimationValue(x, y);
10890 #endif
10891
10892     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10893       MovDir[x][y] = previous_move_direction;
10894
10895 #if USE_NEW_CUSTOM_VALUE
10896     if (element_info[new_element].use_last_ce_value)
10897       CustomValue[x][y] = last_ce_value;
10898 #endif
10899
10900     InitField_WithBug1(x, y, FALSE);
10901
10902     new_element = Feld[x][y];   /* element may have changed */
10903
10904 #if USE_GFX_RESET_GFX_ANIMATION
10905     ResetGfxAnimation(x, y);
10906     ResetRandomAnimationValue(x, y);
10907 #endif
10908
10909     TEST_DrawLevelField(x, y);
10910
10911     if (GFX_CRUMBLED(new_element))
10912       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10913   }
10914
10915 #if 1
10916   /* check if element under the player changes from accessible to unaccessible
10917      (needed for special case of dropping element which then changes) */
10918   /* (must be checked after creating new element for walkable group elements) */
10919 #if USE_FIX_KILLED_BY_NON_WALKABLE
10920   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10921       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10922   {
10923     Bang(x, y);
10924
10925     return;
10926   }
10927 #else
10928   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10929       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10930   {
10931     Bang(x, y);
10932
10933     return;
10934   }
10935 #endif
10936 #endif
10937
10938   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10939   if (new_element_is_player)
10940     RelocatePlayer(x, y, new_element);
10941
10942   if (is_change)
10943     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10944
10945   TestIfBadThingTouchesPlayer(x, y);
10946   TestIfPlayerTouchesCustomElement(x, y);
10947   TestIfElementTouchesCustomElement(x, y);
10948 }
10949
10950 static void CreateField(int x, int y, int element)
10951 {
10952   CreateFieldExt(x, y, element, FALSE);
10953 }
10954
10955 static void CreateElementFromChange(int x, int y, int element)
10956 {
10957   element = GET_VALID_RUNTIME_ELEMENT(element);
10958
10959 #if USE_STOP_CHANGED_ELEMENTS
10960   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10961   {
10962     int old_element = Feld[x][y];
10963
10964     /* prevent changed element from moving in same engine frame
10965        unless both old and new element can either fall or move */
10966     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10967         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10968       Stop[x][y] = TRUE;
10969   }
10970 #endif
10971
10972   CreateFieldExt(x, y, element, TRUE);
10973 }
10974
10975 static boolean ChangeElement(int x, int y, int element, int page)
10976 {
10977   struct ElementInfo *ei = &element_info[element];
10978   struct ElementChangeInfo *change = &ei->change_page[page];
10979   int ce_value = CustomValue[x][y];
10980   int ce_score = ei->collect_score;
10981   int target_element;
10982   int old_element = Feld[x][y];
10983
10984   /* always use default change event to prevent running into a loop */
10985   if (ChangeEvent[x][y] == -1)
10986     ChangeEvent[x][y] = CE_DELAY;
10987
10988   if (ChangeEvent[x][y] == CE_DELAY)
10989   {
10990     /* reset actual trigger element, trigger player and action element */
10991     change->actual_trigger_element = EL_EMPTY;
10992     change->actual_trigger_player = EL_EMPTY;
10993     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10994     change->actual_trigger_side = CH_SIDE_NONE;
10995     change->actual_trigger_ce_value = 0;
10996     change->actual_trigger_ce_score = 0;
10997   }
10998
10999   /* do not change elements more than a specified maximum number of changes */
11000   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11001     return FALSE;
11002
11003   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11004
11005   if (change->explode)
11006   {
11007     Bang(x, y);
11008
11009     return TRUE;
11010   }
11011
11012   if (change->use_target_content)
11013   {
11014     boolean complete_replace = TRUE;
11015     boolean can_replace[3][3];
11016     int xx, yy;
11017
11018     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11019     {
11020       boolean is_empty;
11021       boolean is_walkable;
11022       boolean is_diggable;
11023       boolean is_collectible;
11024       boolean is_removable;
11025       boolean is_destructible;
11026       int ex = x + xx - 1;
11027       int ey = y + yy - 1;
11028       int content_element = change->target_content.e[xx][yy];
11029       int e;
11030
11031       can_replace[xx][yy] = TRUE;
11032
11033       if (ex == x && ey == y)   /* do not check changing element itself */
11034         continue;
11035
11036       if (content_element == EL_EMPTY_SPACE)
11037       {
11038         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11039
11040         continue;
11041       }
11042
11043       if (!IN_LEV_FIELD(ex, ey))
11044       {
11045         can_replace[xx][yy] = FALSE;
11046         complete_replace = FALSE;
11047
11048         continue;
11049       }
11050
11051       e = Feld[ex][ey];
11052
11053       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11054         e = MovingOrBlocked2Element(ex, ey);
11055
11056       is_empty = (IS_FREE(ex, ey) ||
11057                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11058
11059       is_walkable     = (is_empty || IS_WALKABLE(e));
11060       is_diggable     = (is_empty || IS_DIGGABLE(e));
11061       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11062       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11063       is_removable    = (is_diggable || is_collectible);
11064
11065       can_replace[xx][yy] =
11066         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11067           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11068           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11069           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11070           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11071           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11072          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11073
11074       if (!can_replace[xx][yy])
11075         complete_replace = FALSE;
11076     }
11077
11078     if (!change->only_if_complete || complete_replace)
11079     {
11080       boolean something_has_changed = FALSE;
11081
11082       if (change->only_if_complete && change->use_random_replace &&
11083           RND(100) < change->random_percentage)
11084         return FALSE;
11085
11086       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11087       {
11088         int ex = x + xx - 1;
11089         int ey = y + yy - 1;
11090         int content_element;
11091
11092         if (can_replace[xx][yy] && (!change->use_random_replace ||
11093                                     RND(100) < change->random_percentage))
11094         {
11095           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11096             RemoveMovingField(ex, ey);
11097
11098           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11099
11100           content_element = change->target_content.e[xx][yy];
11101           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11102                                               ce_value, ce_score);
11103
11104           CreateElementFromChange(ex, ey, target_element);
11105
11106           something_has_changed = TRUE;
11107
11108           /* for symmetry reasons, freeze newly created border elements */
11109           if (ex != x || ey != y)
11110             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11111         }
11112       }
11113
11114       if (something_has_changed)
11115       {
11116         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11117         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11118       }
11119     }
11120   }
11121   else
11122   {
11123     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11124                                         ce_value, ce_score);
11125
11126     if (element == EL_DIAGONAL_GROWING ||
11127         element == EL_DIAGONAL_SHRINKING)
11128     {
11129       target_element = Store[x][y];
11130
11131       Store[x][y] = EL_EMPTY;
11132     }
11133
11134     CreateElementFromChange(x, y, target_element);
11135
11136     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11137     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11138   }
11139
11140   /* this uses direct change before indirect change */
11141   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11142
11143   return TRUE;
11144 }
11145
11146 #if USE_NEW_DELAYED_ACTION
11147
11148 static void HandleElementChange(int x, int y, int page)
11149 {
11150   int element = MovingOrBlocked2Element(x, y);
11151   struct ElementInfo *ei = &element_info[element];
11152   struct ElementChangeInfo *change = &ei->change_page[page];
11153   boolean handle_action_before_change = FALSE;
11154
11155 #ifdef DEBUG
11156   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11157       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11158   {
11159     printf("\n\n");
11160     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11161            x, y, element, element_info[element].token_name);
11162     printf("HandleElementChange(): This should never happen!\n");
11163     printf("\n\n");
11164   }
11165 #endif
11166
11167   /* this can happen with classic bombs on walkable, changing elements */
11168   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11169   {
11170 #if 0
11171     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11172       ChangeDelay[x][y] = 0;
11173 #endif
11174
11175     return;
11176   }
11177
11178   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11179   {
11180     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11181
11182     if (change->can_change)
11183     {
11184 #if 1
11185       /* !!! not clear why graphic animation should be reset at all here !!! */
11186       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11187 #if USE_GFX_RESET_WHEN_NOT_MOVING
11188       /* when a custom element is about to change (for example by change delay),
11189          do not reset graphic animation when the custom element is moving */
11190       if (!IS_MOVING(x, y))
11191 #endif
11192       {
11193         ResetGfxAnimation(x, y);
11194         ResetRandomAnimationValue(x, y);
11195       }
11196 #endif
11197
11198       if (change->pre_change_function)
11199         change->pre_change_function(x, y);
11200     }
11201   }
11202
11203   ChangeDelay[x][y]--;
11204
11205   if (ChangeDelay[x][y] != 0)           /* continue element change */
11206   {
11207     if (change->can_change)
11208     {
11209       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11210
11211       if (IS_ANIMATED(graphic))
11212         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11213
11214       if (change->change_function)
11215         change->change_function(x, y);
11216     }
11217   }
11218   else                                  /* finish element change */
11219   {
11220     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11221     {
11222       page = ChangePage[x][y];
11223       ChangePage[x][y] = -1;
11224
11225       change = &ei->change_page[page];
11226     }
11227
11228     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11229     {
11230       ChangeDelay[x][y] = 1;            /* try change after next move step */
11231       ChangePage[x][y] = page;          /* remember page to use for change */
11232
11233       return;
11234     }
11235
11236 #if 1
11237     /* special case: set new level random seed before changing element */
11238     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11239       handle_action_before_change = TRUE;
11240
11241     if (change->has_action && handle_action_before_change)
11242       ExecuteCustomElementAction(x, y, element, page);
11243 #endif
11244
11245     if (change->can_change)
11246     {
11247       if (ChangeElement(x, y, element, page))
11248       {
11249         if (change->post_change_function)
11250           change->post_change_function(x, y);
11251       }
11252     }
11253
11254     if (change->has_action && !handle_action_before_change)
11255       ExecuteCustomElementAction(x, y, element, page);
11256   }
11257 }
11258
11259 #else
11260
11261 static void HandleElementChange(int x, int y, int page)
11262 {
11263   int element = MovingOrBlocked2Element(x, y);
11264   struct ElementInfo *ei = &element_info[element];
11265   struct ElementChangeInfo *change = &ei->change_page[page];
11266
11267 #ifdef DEBUG
11268   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11269   {
11270     printf("\n\n");
11271     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11272            x, y, element, element_info[element].token_name);
11273     printf("HandleElementChange(): This should never happen!\n");
11274     printf("\n\n");
11275   }
11276 #endif
11277
11278   /* this can happen with classic bombs on walkable, changing elements */
11279   if (!CAN_CHANGE(element))
11280   {
11281 #if 0
11282     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11283       ChangeDelay[x][y] = 0;
11284 #endif
11285
11286     return;
11287   }
11288
11289   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11290   {
11291     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11292
11293     ResetGfxAnimation(x, y);
11294     ResetRandomAnimationValue(x, y);
11295
11296     if (change->pre_change_function)
11297       change->pre_change_function(x, y);
11298   }
11299
11300   ChangeDelay[x][y]--;
11301
11302   if (ChangeDelay[x][y] != 0)           /* continue element change */
11303   {
11304     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11305
11306     if (IS_ANIMATED(graphic))
11307       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11308
11309     if (change->change_function)
11310       change->change_function(x, y);
11311   }
11312   else                                  /* finish element change */
11313   {
11314     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11315     {
11316       page = ChangePage[x][y];
11317       ChangePage[x][y] = -1;
11318
11319       change = &ei->change_page[page];
11320     }
11321
11322     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11323     {
11324       ChangeDelay[x][y] = 1;            /* try change after next move step */
11325       ChangePage[x][y] = page;          /* remember page to use for change */
11326
11327       return;
11328     }
11329
11330     if (ChangeElement(x, y, element, page))
11331     {
11332       if (change->post_change_function)
11333         change->post_change_function(x, y);
11334     }
11335   }
11336 }
11337
11338 #endif
11339
11340 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11341                                               int trigger_element,
11342                                               int trigger_event,
11343                                               int trigger_player,
11344                                               int trigger_side,
11345                                               int trigger_page)
11346 {
11347   boolean change_done_any = FALSE;
11348   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11349   int i;
11350
11351   if (!(trigger_events[trigger_element][trigger_event]))
11352     return FALSE;
11353
11354 #if 0
11355   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11356          trigger_event, recursion_loop_depth, recursion_loop_detected,
11357          recursion_loop_element, EL_NAME(recursion_loop_element));
11358 #endif
11359
11360   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11361
11362   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11363   {
11364     int element = EL_CUSTOM_START + i;
11365     boolean change_done = FALSE;
11366     int p;
11367
11368     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11369         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11370       continue;
11371
11372     for (p = 0; p < element_info[element].num_change_pages; p++)
11373     {
11374       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11375
11376       if (change->can_change_or_has_action &&
11377           change->has_event[trigger_event] &&
11378           change->trigger_side & trigger_side &&
11379           change->trigger_player & trigger_player &&
11380           change->trigger_page & trigger_page_bits &&
11381           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11382       {
11383         change->actual_trigger_element = trigger_element;
11384         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11385         change->actual_trigger_player_bits = trigger_player;
11386         change->actual_trigger_side = trigger_side;
11387         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11388         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11389
11390 #if 0
11391         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11392                element, EL_NAME(element), p);
11393 #endif
11394
11395         if ((change->can_change && !change_done) || change->has_action)
11396         {
11397           int x, y;
11398
11399           SCAN_PLAYFIELD(x, y)
11400           {
11401             if (Feld[x][y] == element)
11402             {
11403               if (change->can_change && !change_done)
11404               {
11405 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11406                 /* if element already changed in this frame, not only prevent
11407                    another element change (checked in ChangeElement()), but
11408                    also prevent additional element actions for this element */
11409
11410                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11411                     !level.use_action_after_change_bug)
11412                   continue;
11413 #endif
11414
11415 #if 0
11416                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11417                        element, EL_NAME(element), p);
11418 #endif
11419
11420                 ChangeDelay[x][y] = 1;
11421                 ChangeEvent[x][y] = trigger_event;
11422
11423                 HandleElementChange(x, y, p);
11424               }
11425 #if USE_NEW_DELAYED_ACTION
11426               else if (change->has_action)
11427               {
11428 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11429                 /* if element already changed in this frame, not only prevent
11430                    another element change (checked in ChangeElement()), but
11431                    also prevent additional element actions for this element */
11432
11433                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11434                     !level.use_action_after_change_bug)
11435                   continue;
11436 #endif
11437
11438
11439 #if 0
11440                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11441                        element, EL_NAME(element), p);
11442 #endif
11443
11444                 ExecuteCustomElementAction(x, y, element, p);
11445                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11446               }
11447 #else
11448               if (change->has_action)
11449               {
11450                 ExecuteCustomElementAction(x, y, element, p);
11451                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11452               }
11453 #endif
11454             }
11455           }
11456
11457           if (change->can_change)
11458           {
11459             change_done = TRUE;
11460             change_done_any = TRUE;
11461
11462 #if 0
11463             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11464                    element, EL_NAME(element), p);
11465 #endif
11466
11467           }
11468         }
11469       }
11470     }
11471   }
11472
11473   RECURSION_LOOP_DETECTION_END();
11474
11475   return change_done_any;
11476 }
11477
11478 static boolean CheckElementChangeExt(int x, int y,
11479                                      int element,
11480                                      int trigger_element,
11481                                      int trigger_event,
11482                                      int trigger_player,
11483                                      int trigger_side)
11484 {
11485   boolean change_done = FALSE;
11486   int p;
11487
11488   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11489       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11490     return FALSE;
11491
11492   if (Feld[x][y] == EL_BLOCKED)
11493   {
11494     Blocked2Moving(x, y, &x, &y);
11495     element = Feld[x][y];
11496   }
11497
11498 #if 0
11499   /* check if element has already changed */
11500   if (Feld[x][y] != element)
11501     return FALSE;
11502 #else
11503   /* check if element has already changed or is about to change after moving */
11504   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11505        Feld[x][y] != element) ||
11506
11507       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11508        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11509         ChangePage[x][y] != -1)))
11510     return FALSE;
11511 #endif
11512
11513 #if 0
11514   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11515          trigger_event, recursion_loop_depth, recursion_loop_detected,
11516          recursion_loop_element, EL_NAME(recursion_loop_element));
11517 #endif
11518
11519   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11520
11521 #if 0
11522   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11523 #endif
11524
11525   for (p = 0; p < element_info[element].num_change_pages; p++)
11526   {
11527     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11528
11529     /* check trigger element for all events where the element that is checked
11530        for changing interacts with a directly adjacent element -- this is
11531        different to element changes that affect other elements to change on the
11532        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11533     boolean check_trigger_element =
11534       (trigger_event == CE_TOUCHING_X ||
11535        trigger_event == CE_HITTING_X ||
11536        trigger_event == CE_HIT_BY_X ||
11537 #if 1
11538        /* this one was forgotten until 3.2.3 */
11539        trigger_event == CE_DIGGING_X);
11540 #endif
11541
11542     if (change->can_change_or_has_action &&
11543         change->has_event[trigger_event] &&
11544         change->trigger_side & trigger_side &&
11545         change->trigger_player & trigger_player &&
11546         (!check_trigger_element ||
11547          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11548     {
11549       change->actual_trigger_element = trigger_element;
11550       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11551       change->actual_trigger_player_bits = trigger_player;
11552       change->actual_trigger_side = trigger_side;
11553       change->actual_trigger_ce_value = CustomValue[x][y];
11554       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11555
11556       /* special case: trigger element not at (x,y) position for some events */
11557       if (check_trigger_element)
11558       {
11559         static struct
11560         {
11561           int dx, dy;
11562         } move_xy[] =
11563           {
11564             {  0,  0 },
11565             { -1,  0 },
11566             { +1,  0 },
11567             {  0,  0 },
11568             {  0, -1 },
11569             {  0,  0 }, { 0, 0 }, { 0, 0 },
11570             {  0, +1 }
11571           };
11572
11573         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11574         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11575
11576         change->actual_trigger_ce_value = CustomValue[xx][yy];
11577         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11578       }
11579
11580       if (change->can_change && !change_done)
11581       {
11582         ChangeDelay[x][y] = 1;
11583         ChangeEvent[x][y] = trigger_event;
11584
11585         HandleElementChange(x, y, p);
11586
11587         change_done = TRUE;
11588       }
11589 #if USE_NEW_DELAYED_ACTION
11590       else if (change->has_action)
11591       {
11592         ExecuteCustomElementAction(x, y, element, p);
11593         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11594       }
11595 #else
11596       if (change->has_action)
11597       {
11598         ExecuteCustomElementAction(x, y, element, p);
11599         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11600       }
11601 #endif
11602     }
11603   }
11604
11605   RECURSION_LOOP_DETECTION_END();
11606
11607   return change_done;
11608 }
11609
11610 static void PlayPlayerSound(struct PlayerInfo *player)
11611 {
11612   int jx = player->jx, jy = player->jy;
11613   int sound_element = player->artwork_element;
11614   int last_action = player->last_action_waiting;
11615   int action = player->action_waiting;
11616
11617   if (player->is_waiting)
11618   {
11619     if (action != last_action)
11620       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11621     else
11622       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11623   }
11624   else
11625   {
11626     if (action != last_action)
11627       StopSound(element_info[sound_element].sound[last_action]);
11628
11629     if (last_action == ACTION_SLEEPING)
11630       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11631   }
11632 }
11633
11634 static void PlayAllPlayersSound()
11635 {
11636   int i;
11637
11638   for (i = 0; i < MAX_PLAYERS; i++)
11639     if (stored_player[i].active)
11640       PlayPlayerSound(&stored_player[i]);
11641 }
11642
11643 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11644 {
11645   boolean last_waiting = player->is_waiting;
11646   int move_dir = player->MovDir;
11647
11648   player->dir_waiting = move_dir;
11649   player->last_action_waiting = player->action_waiting;
11650
11651   if (is_waiting)
11652   {
11653     if (!last_waiting)          /* not waiting -> waiting */
11654     {
11655       player->is_waiting = TRUE;
11656
11657       player->frame_counter_bored =
11658         FrameCounter +
11659         game.player_boring_delay_fixed +
11660         GetSimpleRandom(game.player_boring_delay_random);
11661       player->frame_counter_sleeping =
11662         FrameCounter +
11663         game.player_sleeping_delay_fixed +
11664         GetSimpleRandom(game.player_sleeping_delay_random);
11665
11666       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11667     }
11668
11669     if (game.player_sleeping_delay_fixed +
11670         game.player_sleeping_delay_random > 0 &&
11671         player->anim_delay_counter == 0 &&
11672         player->post_delay_counter == 0 &&
11673         FrameCounter >= player->frame_counter_sleeping)
11674       player->is_sleeping = TRUE;
11675     else if (game.player_boring_delay_fixed +
11676              game.player_boring_delay_random > 0 &&
11677              FrameCounter >= player->frame_counter_bored)
11678       player->is_bored = TRUE;
11679
11680     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11681                               player->is_bored ? ACTION_BORING :
11682                               ACTION_WAITING);
11683
11684     if (player->is_sleeping && player->use_murphy)
11685     {
11686       /* special case for sleeping Murphy when leaning against non-free tile */
11687
11688       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11689           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11690            !IS_MOVING(player->jx - 1, player->jy)))
11691         move_dir = MV_LEFT;
11692       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11693                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11694                 !IS_MOVING(player->jx + 1, player->jy)))
11695         move_dir = MV_RIGHT;
11696       else
11697         player->is_sleeping = FALSE;
11698
11699       player->dir_waiting = move_dir;
11700     }
11701
11702     if (player->is_sleeping)
11703     {
11704       if (player->num_special_action_sleeping > 0)
11705       {
11706         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11707         {
11708           int last_special_action = player->special_action_sleeping;
11709           int num_special_action = player->num_special_action_sleeping;
11710           int special_action =
11711             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11712              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11713              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11714              last_special_action + 1 : ACTION_SLEEPING);
11715           int special_graphic =
11716             el_act_dir2img(player->artwork_element, special_action, move_dir);
11717
11718           player->anim_delay_counter =
11719             graphic_info[special_graphic].anim_delay_fixed +
11720             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11721           player->post_delay_counter =
11722             graphic_info[special_graphic].post_delay_fixed +
11723             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11724
11725           player->special_action_sleeping = special_action;
11726         }
11727
11728         if (player->anim_delay_counter > 0)
11729         {
11730           player->action_waiting = player->special_action_sleeping;
11731           player->anim_delay_counter--;
11732         }
11733         else if (player->post_delay_counter > 0)
11734         {
11735           player->post_delay_counter--;
11736         }
11737       }
11738     }
11739     else if (player->is_bored)
11740     {
11741       if (player->num_special_action_bored > 0)
11742       {
11743         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11744         {
11745           int special_action =
11746             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11747           int special_graphic =
11748             el_act_dir2img(player->artwork_element, special_action, move_dir);
11749
11750           player->anim_delay_counter =
11751             graphic_info[special_graphic].anim_delay_fixed +
11752             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11753           player->post_delay_counter =
11754             graphic_info[special_graphic].post_delay_fixed +
11755             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11756
11757           player->special_action_bored = special_action;
11758         }
11759
11760         if (player->anim_delay_counter > 0)
11761         {
11762           player->action_waiting = player->special_action_bored;
11763           player->anim_delay_counter--;
11764         }
11765         else if (player->post_delay_counter > 0)
11766         {
11767           player->post_delay_counter--;
11768         }
11769       }
11770     }
11771   }
11772   else if (last_waiting)        /* waiting -> not waiting */
11773   {
11774     player->is_waiting = FALSE;
11775     player->is_bored = FALSE;
11776     player->is_sleeping = FALSE;
11777
11778     player->frame_counter_bored = -1;
11779     player->frame_counter_sleeping = -1;
11780
11781     player->anim_delay_counter = 0;
11782     player->post_delay_counter = 0;
11783
11784     player->dir_waiting = player->MovDir;
11785     player->action_waiting = ACTION_DEFAULT;
11786
11787     player->special_action_bored = ACTION_DEFAULT;
11788     player->special_action_sleeping = ACTION_DEFAULT;
11789   }
11790 }
11791
11792 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11793 {
11794   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11795   int left      = player_action & JOY_LEFT;
11796   int right     = player_action & JOY_RIGHT;
11797   int up        = player_action & JOY_UP;
11798   int down      = player_action & JOY_DOWN;
11799   int button1   = player_action & JOY_BUTTON_1;
11800   int button2   = player_action & JOY_BUTTON_2;
11801   int dx        = (left ? -1 : right ? 1 : 0);
11802   int dy        = (up   ? -1 : down  ? 1 : 0);
11803
11804   if (!player->active || tape.pausing)
11805     return 0;
11806
11807   if (player_action)
11808   {
11809     if (button1)
11810       snapped = SnapField(player, dx, dy);
11811     else
11812     {
11813       if (button2)
11814         dropped = DropElement(player);
11815
11816       moved = MovePlayer(player, dx, dy);
11817     }
11818
11819     if (tape.single_step && tape.recording && !tape.pausing)
11820     {
11821       if (button1 || (dropped && !moved))
11822       {
11823         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11824         SnapField(player, 0, 0);                /* stop snapping */
11825       }
11826     }
11827
11828     SetPlayerWaiting(player, FALSE);
11829
11830     return player_action;
11831   }
11832   else
11833   {
11834     /* no actions for this player (no input at player's configured device) */
11835
11836     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11837     SnapField(player, 0, 0);
11838     CheckGravityMovementWhenNotMoving(player);
11839
11840     if (player->MovPos == 0)
11841       SetPlayerWaiting(player, TRUE);
11842
11843     if (player->MovPos == 0)    /* needed for tape.playing */
11844       player->is_moving = FALSE;
11845
11846     player->is_dropping = FALSE;
11847     player->is_dropping_pressed = FALSE;
11848     player->drop_pressed_delay = 0;
11849
11850     return 0;
11851   }
11852 }
11853
11854 static void CheckLevelTime()
11855 {
11856   int i;
11857
11858   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11859   {
11860     if (level.native_em_level->lev->home == 0)  /* all players at home */
11861     {
11862       PlayerWins(local_player);
11863
11864       AllPlayersGone = TRUE;
11865
11866       level.native_em_level->lev->home = -1;
11867     }
11868
11869     if (level.native_em_level->ply[0]->alive == 0 &&
11870         level.native_em_level->ply[1]->alive == 0 &&
11871         level.native_em_level->ply[2]->alive == 0 &&
11872         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11873       AllPlayersGone = TRUE;
11874   }
11875
11876   if (TimeFrames >= FRAMES_PER_SECOND)
11877   {
11878     TimeFrames = 0;
11879     TapeTime++;
11880
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882     {
11883       struct PlayerInfo *player = &stored_player[i];
11884
11885       if (SHIELD_ON(player))
11886       {
11887         player->shield_normal_time_left--;
11888
11889         if (player->shield_deadly_time_left > 0)
11890           player->shield_deadly_time_left--;
11891       }
11892     }
11893
11894     if (!local_player->LevelSolved && !level.use_step_counter)
11895     {
11896       TimePlayed++;
11897
11898       if (TimeLeft > 0)
11899       {
11900         TimeLeft--;
11901
11902         if (TimeLeft <= 10 && setup.time_limit)
11903           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11904
11905 #if 1
11906         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11907
11908         DisplayGameControlValues();
11909 #else
11910         DrawGameValue_Time(TimeLeft);
11911 #endif
11912
11913         if (!TimeLeft && setup.time_limit)
11914         {
11915           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11916             level.native_em_level->lev->killed_out_of_time = TRUE;
11917           else
11918             for (i = 0; i < MAX_PLAYERS; i++)
11919               KillPlayer(&stored_player[i]);
11920         }
11921       }
11922 #if 1
11923       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11924       {
11925         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11926
11927         DisplayGameControlValues();
11928       }
11929 #else
11930       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11931         DrawGameValue_Time(TimePlayed);
11932 #endif
11933
11934       level.native_em_level->lev->time =
11935         (level.time == 0 ? TimePlayed : TimeLeft);
11936     }
11937
11938     if (tape.recording || tape.playing)
11939       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11940   }
11941
11942 #if 1
11943   UpdateAndDisplayGameControlValues();
11944 #else
11945   UpdateGameDoorValues();
11946   DrawGameDoorValues();
11947 #endif
11948 }
11949
11950 void AdvanceFrameAndPlayerCounters(int player_nr)
11951 {
11952   int i;
11953
11954   /* advance frame counters (global frame counter and time frame counter) */
11955   FrameCounter++;
11956   TimeFrames++;
11957
11958   /* advance player counters (counters for move delay, move animation etc.) */
11959   for (i = 0; i < MAX_PLAYERS; i++)
11960   {
11961     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11962     int move_delay_value = stored_player[i].move_delay_value;
11963     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11964
11965     if (!advance_player_counters)       /* not all players may be affected */
11966       continue;
11967
11968 #if USE_NEW_PLAYER_ANIM
11969     if (move_frames == 0)       /* less than one move per game frame */
11970     {
11971       int stepsize = TILEX / move_delay_value;
11972       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11973       int count = (stored_player[i].is_moving ?
11974                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11975
11976       if (count % delay == 0)
11977         move_frames = 1;
11978     }
11979 #endif
11980
11981     stored_player[i].Frame += move_frames;
11982
11983     if (stored_player[i].MovPos != 0)
11984       stored_player[i].StepFrame += move_frames;
11985
11986     if (stored_player[i].move_delay > 0)
11987       stored_player[i].move_delay--;
11988
11989     /* due to bugs in previous versions, counter must count up, not down */
11990     if (stored_player[i].push_delay != -1)
11991       stored_player[i].push_delay++;
11992
11993     if (stored_player[i].drop_delay > 0)
11994       stored_player[i].drop_delay--;
11995
11996     if (stored_player[i].is_dropping_pressed)
11997       stored_player[i].drop_pressed_delay++;
11998   }
11999 }
12000
12001 void StartGameActions(boolean init_network_game, boolean record_tape,
12002                       long random_seed)
12003 {
12004   unsigned long new_random_seed = InitRND(random_seed);
12005
12006   if (record_tape)
12007     TapeStartRecording(new_random_seed);
12008
12009 #if defined(NETWORK_AVALIABLE)
12010   if (init_network_game)
12011   {
12012     SendToServer_StartPlaying();
12013
12014     return;
12015   }
12016 #endif
12017
12018   InitGame();
12019 }
12020
12021 void GameActions()
12022 {
12023   static unsigned long game_frame_delay = 0;
12024   unsigned long game_frame_delay_value;
12025   byte *recorded_player_action;
12026   byte summarized_player_action = 0;
12027   byte tape_action[MAX_PLAYERS];
12028   int i;
12029
12030   /* detect endless loops, caused by custom element programming */
12031   if (recursion_loop_detected && recursion_loop_depth == 0)
12032   {
12033     char *message = getStringCat3("Internal Error ! Element ",
12034                                   EL_NAME(recursion_loop_element),
12035                                   " caused endless loop ! Quit the game ?");
12036
12037     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12038           EL_NAME(recursion_loop_element));
12039
12040     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12041
12042     recursion_loop_detected = FALSE;    /* if game should be continued */
12043
12044     free(message);
12045
12046     return;
12047   }
12048
12049   if (game.restart_level)
12050     StartGameActions(options.network, setup.autorecord, level.random_seed);
12051
12052   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12053   {
12054     if (level.native_em_level->lev->home == 0)  /* all players at home */
12055     {
12056       PlayerWins(local_player);
12057
12058       AllPlayersGone = TRUE;
12059
12060       level.native_em_level->lev->home = -1;
12061     }
12062
12063     if (level.native_em_level->ply[0]->alive == 0 &&
12064         level.native_em_level->ply[1]->alive == 0 &&
12065         level.native_em_level->ply[2]->alive == 0 &&
12066         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12067       AllPlayersGone = TRUE;
12068   }
12069
12070   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12071     GameWon();
12072
12073   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12074     TapeStop();
12075
12076   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12077     return;
12078
12079   game_frame_delay_value =
12080     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12081
12082   if (tape.playing && tape.warp_forward && !tape.pausing)
12083     game_frame_delay_value = 0;
12084
12085   /* ---------- main game synchronization point ---------- */
12086
12087   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12088
12089   if (network_playing && !network_player_action_received)
12090   {
12091     /* try to get network player actions in time */
12092
12093 #if defined(NETWORK_AVALIABLE)
12094     /* last chance to get network player actions without main loop delay */
12095     HandleNetworking();
12096 #endif
12097
12098     /* game was quit by network peer */
12099     if (game_status != GAME_MODE_PLAYING)
12100       return;
12101
12102     if (!network_player_action_received)
12103       return;           /* failed to get network player actions in time */
12104
12105     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12106   }
12107
12108   if (tape.pausing)
12109     return;
12110
12111   /* at this point we know that we really continue executing the game */
12112
12113   network_player_action_received = FALSE;
12114
12115   /* when playing tape, read previously recorded player input from tape data */
12116   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12117
12118 #if 1
12119   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12120   if (tape.pausing)
12121     return;
12122 #endif
12123
12124   if (tape.set_centered_player)
12125   {
12126     game.centered_player_nr_next = tape.centered_player_nr_next;
12127     game.set_centered_player = TRUE;
12128   }
12129
12130   for (i = 0; i < MAX_PLAYERS; i++)
12131   {
12132     summarized_player_action |= stored_player[i].action;
12133
12134     if (!network_playing)
12135       stored_player[i].effective_action = stored_player[i].action;
12136   }
12137
12138 #if defined(NETWORK_AVALIABLE)
12139   if (network_playing)
12140     SendToServer_MovePlayer(summarized_player_action);
12141 #endif
12142
12143   if (!options.network && !setup.team_mode)
12144     local_player->effective_action = summarized_player_action;
12145
12146   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12147   {
12148     for (i = 0; i < MAX_PLAYERS; i++)
12149       stored_player[i].effective_action =
12150         (i == game.centered_player_nr ? summarized_player_action : 0);
12151   }
12152
12153   if (recorded_player_action != NULL)
12154     for (i = 0; i < MAX_PLAYERS; i++)
12155       stored_player[i].effective_action = recorded_player_action[i];
12156
12157   for (i = 0; i < MAX_PLAYERS; i++)
12158   {
12159     tape_action[i] = stored_player[i].effective_action;
12160
12161     /* (this can only happen in the R'n'D game engine) */
12162     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12163       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12164   }
12165
12166   /* only record actions from input devices, but not programmed actions */
12167   if (tape.recording)
12168     TapeRecordAction(tape_action);
12169
12170   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12171   {
12172     GameActions_EM_Main();
12173   }
12174   else
12175   {
12176     GameActions_RND();
12177   }
12178 }
12179
12180 void GameActions_EM_Main()
12181 {
12182   byte effective_action[MAX_PLAYERS];
12183   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12184   int i;
12185
12186   for (i = 0; i < MAX_PLAYERS; i++)
12187     effective_action[i] = stored_player[i].effective_action;
12188
12189   GameActions_EM(effective_action, warp_mode);
12190
12191   CheckLevelTime();
12192
12193   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12194 }
12195
12196 void GameActions_RND()
12197 {
12198   int magic_wall_x = 0, magic_wall_y = 0;
12199   int i, x, y, element, graphic;
12200
12201   InitPlayfieldScanModeVars();
12202
12203 #if USE_ONE_MORE_CHANGE_PER_FRAME
12204   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12205   {
12206     SCAN_PLAYFIELD(x, y)
12207     {
12208       ChangeCount[x][y] = 0;
12209       ChangeEvent[x][y] = -1;
12210     }
12211   }
12212 #endif
12213
12214   if (game.set_centered_player)
12215   {
12216     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12217
12218     /* switching to "all players" only possible if all players fit to screen */
12219     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12220     {
12221       game.centered_player_nr_next = game.centered_player_nr;
12222       game.set_centered_player = FALSE;
12223     }
12224
12225     /* do not switch focus to non-existing (or non-active) player */
12226     if (game.centered_player_nr_next >= 0 &&
12227         !stored_player[game.centered_player_nr_next].active)
12228     {
12229       game.centered_player_nr_next = game.centered_player_nr;
12230       game.set_centered_player = FALSE;
12231     }
12232   }
12233
12234   if (game.set_centered_player &&
12235       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12236   {
12237     int sx, sy;
12238
12239     if (game.centered_player_nr_next == -1)
12240     {
12241       setScreenCenteredToAllPlayers(&sx, &sy);
12242     }
12243     else
12244     {
12245       sx = stored_player[game.centered_player_nr_next].jx;
12246       sy = stored_player[game.centered_player_nr_next].jy;
12247     }
12248
12249     game.centered_player_nr = game.centered_player_nr_next;
12250     game.set_centered_player = FALSE;
12251
12252     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12253     DrawGameDoorValues();
12254   }
12255
12256   for (i = 0; i < MAX_PLAYERS; i++)
12257   {
12258     int actual_player_action = stored_player[i].effective_action;
12259
12260 #if 1
12261     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12262        - rnd_equinox_tetrachloride 048
12263        - rnd_equinox_tetrachloride_ii 096
12264        - rnd_emanuel_schmieg 002
12265        - doctor_sloan_ww 001, 020
12266     */
12267     if (stored_player[i].MovPos == 0)
12268       CheckGravityMovement(&stored_player[i]);
12269 #endif
12270
12271     /* overwrite programmed action with tape action */
12272     if (stored_player[i].programmed_action)
12273       actual_player_action = stored_player[i].programmed_action;
12274
12275     PlayerActions(&stored_player[i], actual_player_action);
12276
12277     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12278   }
12279
12280   ScrollScreen(NULL, SCROLL_GO_ON);
12281
12282   /* for backwards compatibility, the following code emulates a fixed bug that
12283      occured when pushing elements (causing elements that just made their last
12284      pushing step to already (if possible) make their first falling step in the
12285      same game frame, which is bad); this code is also needed to use the famous
12286      "spring push bug" which is used in older levels and might be wanted to be
12287      used also in newer levels, but in this case the buggy pushing code is only
12288      affecting the "spring" element and no other elements */
12289
12290   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12291   {
12292     for (i = 0; i < MAX_PLAYERS; i++)
12293     {
12294       struct PlayerInfo *player = &stored_player[i];
12295       int x = player->jx;
12296       int y = player->jy;
12297
12298       if (player->active && player->is_pushing && player->is_moving &&
12299           IS_MOVING(x, y) &&
12300           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12301            Feld[x][y] == EL_SPRING))
12302       {
12303         ContinueMoving(x, y);
12304
12305         /* continue moving after pushing (this is actually a bug) */
12306         if (!IS_MOVING(x, y))
12307           Stop[x][y] = FALSE;
12308       }
12309     }
12310   }
12311
12312 #if 0
12313   debug_print_timestamp(0, "start main loop profiling");
12314 #endif
12315
12316   SCAN_PLAYFIELD(x, y)
12317   {
12318     ChangeCount[x][y] = 0;
12319     ChangeEvent[x][y] = -1;
12320
12321     /* this must be handled before main playfield loop */
12322     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12323     {
12324       MovDelay[x][y]--;
12325       if (MovDelay[x][y] <= 0)
12326         RemoveField(x, y);
12327     }
12328
12329 #if USE_NEW_SNAP_DELAY
12330     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12331     {
12332       MovDelay[x][y]--;
12333       if (MovDelay[x][y] <= 0)
12334       {
12335         RemoveField(x, y);
12336         TEST_DrawLevelField(x, y);
12337
12338         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12339       }
12340     }
12341 #endif
12342
12343 #if DEBUG
12344     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12345     {
12346       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12347       printf("GameActions(): This should never happen!\n");
12348
12349       ChangePage[x][y] = -1;
12350     }
12351 #endif
12352
12353     Stop[x][y] = FALSE;
12354     if (WasJustMoving[x][y] > 0)
12355       WasJustMoving[x][y]--;
12356     if (WasJustFalling[x][y] > 0)
12357       WasJustFalling[x][y]--;
12358     if (CheckCollision[x][y] > 0)
12359       CheckCollision[x][y]--;
12360     if (CheckImpact[x][y] > 0)
12361       CheckImpact[x][y]--;
12362
12363     GfxFrame[x][y]++;
12364
12365     /* reset finished pushing action (not done in ContinueMoving() to allow
12366        continuous pushing animation for elements with zero push delay) */
12367     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12368     {
12369       ResetGfxAnimation(x, y);
12370       TEST_DrawLevelField(x, y);
12371     }
12372
12373 #if DEBUG
12374     if (IS_BLOCKED(x, y))
12375     {
12376       int oldx, oldy;
12377
12378       Blocked2Moving(x, y, &oldx, &oldy);
12379       if (!IS_MOVING(oldx, oldy))
12380       {
12381         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12382         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12383         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12384         printf("GameActions(): This should never happen!\n");
12385       }
12386     }
12387 #endif
12388   }
12389
12390 #if 0
12391   debug_print_timestamp(0, "- time for pre-main loop:");
12392 #endif
12393
12394 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12395   SCAN_PLAYFIELD(x, y)
12396   {
12397     element = Feld[x][y];
12398     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12399
12400 #if 1
12401     {
12402 #if 1
12403       int element2 = element;
12404       int graphic2 = graphic;
12405 #else
12406       int element2 = Feld[x][y];
12407       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12408 #endif
12409       int last_gfx_frame = GfxFrame[x][y];
12410
12411       if (graphic_info[graphic2].anim_global_sync)
12412         GfxFrame[x][y] = FrameCounter;
12413       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12414         GfxFrame[x][y] = CustomValue[x][y];
12415       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12416         GfxFrame[x][y] = element_info[element2].collect_score;
12417       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12418         GfxFrame[x][y] = ChangeDelay[x][y];
12419
12420       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12421         DrawLevelGraphicAnimation(x, y, graphic2);
12422     }
12423 #else
12424     ResetGfxFrame(x, y, TRUE);
12425 #endif
12426
12427 #if 1
12428     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12429         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12430       ResetRandomAnimationValue(x, y);
12431 #endif
12432
12433 #if 1
12434     SetRandomAnimationValue(x, y);
12435 #endif
12436
12437 #if 1
12438     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12439 #endif
12440   }
12441 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12442
12443 #if 0
12444   debug_print_timestamp(0, "- time for TEST loop:     -->");
12445 #endif
12446
12447   SCAN_PLAYFIELD(x, y)
12448   {
12449     element = Feld[x][y];
12450     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12451
12452     ResetGfxFrame(x, y, TRUE);
12453
12454     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12455         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12456       ResetRandomAnimationValue(x, y);
12457
12458     SetRandomAnimationValue(x, y);
12459
12460     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12461
12462     if (IS_INACTIVE(element))
12463     {
12464       if (IS_ANIMATED(graphic))
12465         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12466
12467       continue;
12468     }
12469
12470     /* this may take place after moving, so 'element' may have changed */
12471     if (IS_CHANGING(x, y) &&
12472         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12473     {
12474       int page = element_info[element].event_page_nr[CE_DELAY];
12475
12476 #if 1
12477       HandleElementChange(x, y, page);
12478 #else
12479       if (CAN_CHANGE(element))
12480         HandleElementChange(x, y, page);
12481
12482       if (HAS_ACTION(element))
12483         ExecuteCustomElementAction(x, y, element, page);
12484 #endif
12485
12486       element = Feld[x][y];
12487       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12488     }
12489
12490 #if 0   // ---------------------------------------------------------------------
12491
12492     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12493     {
12494       StartMoving(x, y);
12495
12496       element = Feld[x][y];
12497       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12498
12499       if (IS_ANIMATED(graphic) &&
12500           !IS_MOVING(x, y) &&
12501           !Stop[x][y])
12502         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12503
12504       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12505         TEST_DrawTwinkleOnField(x, y);
12506     }
12507     else if (IS_MOVING(x, y))
12508       ContinueMoving(x, y);
12509     else
12510     {
12511       switch (element)
12512       {
12513         case EL_ACID:
12514         case EL_EXIT_OPEN:
12515         case EL_EM_EXIT_OPEN:
12516         case EL_SP_EXIT_OPEN:
12517         case EL_STEEL_EXIT_OPEN:
12518         case EL_EM_STEEL_EXIT_OPEN:
12519         case EL_SP_TERMINAL:
12520         case EL_SP_TERMINAL_ACTIVE:
12521         case EL_EXTRA_TIME:
12522         case EL_SHIELD_NORMAL:
12523         case EL_SHIELD_DEADLY:
12524           if (IS_ANIMATED(graphic))
12525             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12526           break;
12527
12528         case EL_DYNAMITE_ACTIVE:
12529         case EL_EM_DYNAMITE_ACTIVE:
12530         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12531         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12532         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12533         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12534         case EL_SP_DISK_RED_ACTIVE:
12535           CheckDynamite(x, y);
12536           break;
12537
12538         case EL_AMOEBA_GROWING:
12539           AmoebeWaechst(x, y);
12540           break;
12541
12542         case EL_AMOEBA_SHRINKING:
12543           AmoebaDisappearing(x, y);
12544           break;
12545
12546 #if !USE_NEW_AMOEBA_CODE
12547         case EL_AMOEBA_WET:
12548         case EL_AMOEBA_DRY:
12549         case EL_AMOEBA_FULL:
12550         case EL_BD_AMOEBA:
12551         case EL_EMC_DRIPPER:
12552           AmoebeAbleger(x, y);
12553           break;
12554 #endif
12555
12556         case EL_GAME_OF_LIFE:
12557         case EL_BIOMAZE:
12558           Life(x, y);
12559           break;
12560
12561         case EL_EXIT_CLOSED:
12562           CheckExit(x, y);
12563           break;
12564
12565         case EL_EM_EXIT_CLOSED:
12566           CheckExitEM(x, y);
12567           break;
12568
12569         case EL_STEEL_EXIT_CLOSED:
12570           CheckExitSteel(x, y);
12571           break;
12572
12573         case EL_EM_STEEL_EXIT_CLOSED:
12574           CheckExitSteelEM(x, y);
12575           break;
12576
12577         case EL_SP_EXIT_CLOSED:
12578           CheckExitSP(x, y);
12579           break;
12580
12581         case EL_EXPANDABLE_WALL_GROWING:
12582         case EL_EXPANDABLE_STEELWALL_GROWING:
12583           MauerWaechst(x, y);
12584           break;
12585
12586         case EL_EXPANDABLE_WALL:
12587         case EL_EXPANDABLE_WALL_HORIZONTAL:
12588         case EL_EXPANDABLE_WALL_VERTICAL:
12589         case EL_EXPANDABLE_WALL_ANY:
12590         case EL_BD_EXPANDABLE_WALL:
12591           MauerAbleger(x, y);
12592           break;
12593
12594         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12595         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12596         case EL_EXPANDABLE_STEELWALL_ANY:
12597           MauerAblegerStahl(x, y);
12598           break;
12599
12600         case EL_FLAMES:
12601           CheckForDragon(x, y);
12602           break;
12603
12604         case EL_EXPLOSION:
12605           break;
12606
12607         case EL_ELEMENT_SNAPPING:
12608         case EL_DIAGONAL_SHRINKING:
12609         case EL_DIAGONAL_GROWING:
12610         {
12611           graphic =
12612             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12613
12614           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12615           break;
12616         }
12617
12618         default:
12619           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12620             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12621           break;
12622       }
12623     }
12624
12625 #else   // ---------------------------------------------------------------------
12626
12627     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12628     {
12629       StartMoving(x, y);
12630
12631       element = Feld[x][y];
12632       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12633
12634       if (IS_ANIMATED(graphic) &&
12635           !IS_MOVING(x, y) &&
12636           !Stop[x][y])
12637         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12638
12639       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12640         TEST_DrawTwinkleOnField(x, y);
12641     }
12642     else if ((element == EL_ACID ||
12643               element == EL_EXIT_OPEN ||
12644               element == EL_EM_EXIT_OPEN ||
12645               element == EL_SP_EXIT_OPEN ||
12646               element == EL_STEEL_EXIT_OPEN ||
12647               element == EL_EM_STEEL_EXIT_OPEN ||
12648               element == EL_SP_TERMINAL ||
12649               element == EL_SP_TERMINAL_ACTIVE ||
12650               element == EL_EXTRA_TIME ||
12651               element == EL_SHIELD_NORMAL ||
12652               element == EL_SHIELD_DEADLY) &&
12653              IS_ANIMATED(graphic))
12654       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12655     else if (IS_MOVING(x, y))
12656       ContinueMoving(x, y);
12657     else if (IS_ACTIVE_BOMB(element))
12658       CheckDynamite(x, y);
12659     else if (element == EL_AMOEBA_GROWING)
12660       AmoebeWaechst(x, y);
12661     else if (element == EL_AMOEBA_SHRINKING)
12662       AmoebaDisappearing(x, y);
12663
12664 #if !USE_NEW_AMOEBA_CODE
12665     else if (IS_AMOEBALIVE(element))
12666       AmoebeAbleger(x, y);
12667 #endif
12668
12669     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12670       Life(x, y);
12671     else if (element == EL_EXIT_CLOSED)
12672       CheckExit(x, y);
12673     else if (element == EL_EM_EXIT_CLOSED)
12674       CheckExitEM(x, y);
12675     else if (element == EL_STEEL_EXIT_CLOSED)
12676       CheckExitSteel(x, y);
12677     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12678       CheckExitSteelEM(x, y);
12679     else if (element == EL_SP_EXIT_CLOSED)
12680       CheckExitSP(x, y);
12681     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12682              element == EL_EXPANDABLE_STEELWALL_GROWING)
12683       MauerWaechst(x, y);
12684     else if (element == EL_EXPANDABLE_WALL ||
12685              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12686              element == EL_EXPANDABLE_WALL_VERTICAL ||
12687              element == EL_EXPANDABLE_WALL_ANY ||
12688              element == EL_BD_EXPANDABLE_WALL)
12689       MauerAbleger(x, y);
12690     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12691              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12692              element == EL_EXPANDABLE_STEELWALL_ANY)
12693       MauerAblegerStahl(x, y);
12694     else if (element == EL_FLAMES)
12695       CheckForDragon(x, y);
12696     else if (element == EL_EXPLOSION)
12697       ; /* drawing of correct explosion animation is handled separately */
12698     else if (element == EL_ELEMENT_SNAPPING ||
12699              element == EL_DIAGONAL_SHRINKING ||
12700              element == EL_DIAGONAL_GROWING)
12701     {
12702       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12703
12704       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12705     }
12706     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12707       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12708
12709 #endif  // ---------------------------------------------------------------------
12710
12711     if (IS_BELT_ACTIVE(element))
12712       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12713
12714     if (game.magic_wall_active)
12715     {
12716       int jx = local_player->jx, jy = local_player->jy;
12717
12718       /* play the element sound at the position nearest to the player */
12719       if ((element == EL_MAGIC_WALL_FULL ||
12720            element == EL_MAGIC_WALL_ACTIVE ||
12721            element == EL_MAGIC_WALL_EMPTYING ||
12722            element == EL_BD_MAGIC_WALL_FULL ||
12723            element == EL_BD_MAGIC_WALL_ACTIVE ||
12724            element == EL_BD_MAGIC_WALL_EMPTYING ||
12725            element == EL_DC_MAGIC_WALL_FULL ||
12726            element == EL_DC_MAGIC_WALL_ACTIVE ||
12727            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12728           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12729       {
12730         magic_wall_x = x;
12731         magic_wall_y = y;
12732       }
12733     }
12734   }
12735
12736 #if 0
12737   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12738 #endif
12739
12740 #if USE_NEW_AMOEBA_CODE
12741   /* new experimental amoeba growth stuff */
12742   if (!(FrameCounter % 8))
12743   {
12744     static unsigned long random = 1684108901;
12745
12746     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12747     {
12748       x = RND(lev_fieldx);
12749       y = RND(lev_fieldy);
12750       element = Feld[x][y];
12751
12752       if (!IS_PLAYER(x,y) &&
12753           (element == EL_EMPTY ||
12754            CAN_GROW_INTO(element) ||
12755            element == EL_QUICKSAND_EMPTY ||
12756            element == EL_QUICKSAND_FAST_EMPTY ||
12757            element == EL_ACID_SPLASH_LEFT ||
12758            element == EL_ACID_SPLASH_RIGHT))
12759       {
12760         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12761             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12762             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12763             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12764           Feld[x][y] = EL_AMOEBA_DROP;
12765       }
12766
12767       random = random * 129 + 1;
12768     }
12769   }
12770 #endif
12771
12772 #if 0
12773   if (game.explosions_delayed)
12774 #endif
12775   {
12776     game.explosions_delayed = FALSE;
12777
12778     SCAN_PLAYFIELD(x, y)
12779     {
12780       element = Feld[x][y];
12781
12782       if (ExplodeField[x][y])
12783         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12784       else if (element == EL_EXPLOSION)
12785         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12786
12787       ExplodeField[x][y] = EX_TYPE_NONE;
12788     }
12789
12790     game.explosions_delayed = TRUE;
12791   }
12792
12793   if (game.magic_wall_active)
12794   {
12795     if (!(game.magic_wall_time_left % 4))
12796     {
12797       int element = Feld[magic_wall_x][magic_wall_y];
12798
12799       if (element == EL_BD_MAGIC_WALL_FULL ||
12800           element == EL_BD_MAGIC_WALL_ACTIVE ||
12801           element == EL_BD_MAGIC_WALL_EMPTYING)
12802         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12803       else if (element == EL_DC_MAGIC_WALL_FULL ||
12804                element == EL_DC_MAGIC_WALL_ACTIVE ||
12805                element == EL_DC_MAGIC_WALL_EMPTYING)
12806         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12807       else
12808         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12809     }
12810
12811     if (game.magic_wall_time_left > 0)
12812     {
12813       game.magic_wall_time_left--;
12814
12815       if (!game.magic_wall_time_left)
12816       {
12817         SCAN_PLAYFIELD(x, y)
12818         {
12819           element = Feld[x][y];
12820
12821           if (element == EL_MAGIC_WALL_ACTIVE ||
12822               element == EL_MAGIC_WALL_FULL)
12823           {
12824             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12825             TEST_DrawLevelField(x, y);
12826           }
12827           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12828                    element == EL_BD_MAGIC_WALL_FULL)
12829           {
12830             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12831             TEST_DrawLevelField(x, y);
12832           }
12833           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12834                    element == EL_DC_MAGIC_WALL_FULL)
12835           {
12836             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12837             TEST_DrawLevelField(x, y);
12838           }
12839         }
12840
12841         game.magic_wall_active = FALSE;
12842       }
12843     }
12844   }
12845
12846   if (game.light_time_left > 0)
12847   {
12848     game.light_time_left--;
12849
12850     if (game.light_time_left == 0)
12851       RedrawAllLightSwitchesAndInvisibleElements();
12852   }
12853
12854   if (game.timegate_time_left > 0)
12855   {
12856     game.timegate_time_left--;
12857
12858     if (game.timegate_time_left == 0)
12859       CloseAllOpenTimegates();
12860   }
12861
12862   if (game.lenses_time_left > 0)
12863   {
12864     game.lenses_time_left--;
12865
12866     if (game.lenses_time_left == 0)
12867       RedrawAllInvisibleElementsForLenses();
12868   }
12869
12870   if (game.magnify_time_left > 0)
12871   {
12872     game.magnify_time_left--;
12873
12874     if (game.magnify_time_left == 0)
12875       RedrawAllInvisibleElementsForMagnifier();
12876   }
12877
12878   for (i = 0; i < MAX_PLAYERS; i++)
12879   {
12880     struct PlayerInfo *player = &stored_player[i];
12881
12882     if (SHIELD_ON(player))
12883     {
12884       if (player->shield_deadly_time_left)
12885         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12886       else if (player->shield_normal_time_left)
12887         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12888     }
12889   }
12890
12891 #if USE_DELAYED_GFX_REDRAW
12892   SCAN_PLAYFIELD(x, y)
12893   {
12894 #if 1
12895     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12896 #else
12897     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12898         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12899 #endif
12900     {
12901       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12902          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12903
12904       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12905         DrawLevelField(x, y);
12906
12907       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12908         DrawLevelFieldCrumbledSand(x, y);
12909
12910       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12911         DrawLevelFieldCrumbledSandNeighbours(x, y);
12912
12913       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12914         DrawTwinkleOnField(x, y);
12915     }
12916
12917     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12918   }
12919 #endif
12920
12921   CheckLevelTime();
12922
12923   DrawAllPlayers();
12924   PlayAllPlayersSound();
12925
12926   if (options.debug)                    /* calculate frames per second */
12927   {
12928     static unsigned long fps_counter = 0;
12929     static int fps_frames = 0;
12930     unsigned long fps_delay_ms = Counter() - fps_counter;
12931
12932     fps_frames++;
12933
12934     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12935     {
12936       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12937
12938       fps_frames = 0;
12939       fps_counter = Counter();
12940     }
12941
12942     redraw_mask |= REDRAW_FPS;
12943   }
12944
12945   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12946
12947   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12948   {
12949     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12950
12951     local_player->show_envelope = 0;
12952   }
12953
12954 #if 0
12955   debug_print_timestamp(0, "stop main loop profiling ");
12956   printf("----------------------------------------------------------\n");
12957 #endif
12958
12959   /* use random number generator in every frame to make it less predictable */
12960   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12961     RND(1);
12962 }
12963
12964 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12965 {
12966   int min_x = x, min_y = y, max_x = x, max_y = y;
12967   int i;
12968
12969   for (i = 0; i < MAX_PLAYERS; i++)
12970   {
12971     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12972
12973     if (!stored_player[i].active || &stored_player[i] == player)
12974       continue;
12975
12976     min_x = MIN(min_x, jx);
12977     min_y = MIN(min_y, jy);
12978     max_x = MAX(max_x, jx);
12979     max_y = MAX(max_y, jy);
12980   }
12981
12982   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12983 }
12984
12985 static boolean AllPlayersInVisibleScreen()
12986 {
12987   int i;
12988
12989   for (i = 0; i < MAX_PLAYERS; i++)
12990   {
12991     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12992
12993     if (!stored_player[i].active)
12994       continue;
12995
12996     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12997       return FALSE;
12998   }
12999
13000   return TRUE;
13001 }
13002
13003 void ScrollLevel(int dx, int dy)
13004 {
13005 #if 0
13006   /* (directly solved in BlitBitmap() now) */
13007   static Bitmap *bitmap_db_field2 = NULL;
13008   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13009   int x, y;
13010 #else
13011   int x, y;
13012 #endif
13013
13014 #if 0
13015   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13016   /* only horizontal XOR vertical scroll direction allowed */
13017   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13018     return;
13019 #endif
13020
13021 #if 0
13022   /* (directly solved in BlitBitmap() now) */
13023   if (bitmap_db_field2 == NULL)
13024     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13025
13026   /* needed when blitting directly to same bitmap -- should not be needed with
13027      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13028   BlitBitmap(drawto_field, bitmap_db_field2,
13029              FX + TILEX * (dx == -1) - softscroll_offset,
13030              FY + TILEY * (dy == -1) - softscroll_offset,
13031              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13032              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13033              FX + TILEX * (dx == 1) - softscroll_offset,
13034              FY + TILEY * (dy == 1) - softscroll_offset);
13035   BlitBitmap(bitmap_db_field2, drawto_field,
13036              FX + TILEX * (dx == 1) - softscroll_offset,
13037              FY + TILEY * (dy == 1) - softscroll_offset,
13038              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13039              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13040              FX + TILEX * (dx == 1) - softscroll_offset,
13041              FY + TILEY * (dy == 1) - softscroll_offset);
13042
13043 #else
13044
13045 #if 0
13046   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13047   int xsize = (BX2 - BX1 + 1);
13048   int ysize = (BY2 - BY1 + 1);
13049   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13050   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13051   int step  = (start < end ? +1 : -1);
13052
13053   for (i = start; i != end; i += step)
13054   {
13055     BlitBitmap(drawto_field, drawto_field,
13056                FX + TILEX * (dx != 0 ? i + step : 0),
13057                FY + TILEY * (dy != 0 ? i + step : 0),
13058                TILEX * (dx != 0 ? 1 : xsize),
13059                TILEY * (dy != 0 ? 1 : ysize),
13060                FX + TILEX * (dx != 0 ? i : 0),
13061                FY + TILEY * (dy != 0 ? i : 0));
13062   }
13063
13064 #else
13065
13066   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13067
13068   BlitBitmap(drawto_field, drawto_field,
13069              FX + TILEX * (dx == -1) - softscroll_offset,
13070              FY + TILEY * (dy == -1) - softscroll_offset,
13071              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13072              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13073              FX + TILEX * (dx == 1) - softscroll_offset,
13074              FY + TILEY * (dy == 1) - softscroll_offset);
13075 #endif
13076 #endif
13077
13078   if (dx != 0)
13079   {
13080     x = (dx == 1 ? BX1 : BX2);
13081     for (y = BY1; y <= BY2; y++)
13082       DrawScreenField(x, y);
13083   }
13084
13085   if (dy != 0)
13086   {
13087     y = (dy == 1 ? BY1 : BY2);
13088     for (x = BX1; x <= BX2; x++)
13089       DrawScreenField(x, y);
13090   }
13091
13092   redraw_mask |= REDRAW_FIELD;
13093 }
13094
13095 static boolean canFallDown(struct PlayerInfo *player)
13096 {
13097   int jx = player->jx, jy = player->jy;
13098
13099   return (IN_LEV_FIELD(jx, jy + 1) &&
13100           (IS_FREE(jx, jy + 1) ||
13101            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13102           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13103           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13104 }
13105
13106 static boolean canPassField(int x, int y, int move_dir)
13107 {
13108   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13109   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13110   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13111   int nextx = x + dx;
13112   int nexty = y + dy;
13113   int element = Feld[x][y];
13114
13115   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13116           !CAN_MOVE(element) &&
13117           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13118           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13119           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13120 }
13121
13122 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13123 {
13124   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13125   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13126   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13127   int newx = x + dx;
13128   int newy = y + dy;
13129
13130   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13131           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13132           (IS_DIGGABLE(Feld[newx][newy]) ||
13133            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13134            canPassField(newx, newy, move_dir)));
13135 }
13136
13137 static void CheckGravityMovement(struct PlayerInfo *player)
13138 {
13139 #if USE_PLAYER_GRAVITY
13140   if (player->gravity && !player->programmed_action)
13141 #else
13142   if (game.gravity && !player->programmed_action)
13143 #endif
13144   {
13145     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13146     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13147     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13148     int jx = player->jx, jy = player->jy;
13149     boolean player_is_moving_to_valid_field =
13150       (!player_is_snapping &&
13151        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13152         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13153     boolean player_can_fall_down = canFallDown(player);
13154
13155     if (player_can_fall_down &&
13156         !player_is_moving_to_valid_field)
13157       player->programmed_action = MV_DOWN;
13158   }
13159 }
13160
13161 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13162 {
13163   return CheckGravityMovement(player);
13164
13165 #if USE_PLAYER_GRAVITY
13166   if (player->gravity && !player->programmed_action)
13167 #else
13168   if (game.gravity && !player->programmed_action)
13169 #endif
13170   {
13171     int jx = player->jx, jy = player->jy;
13172     boolean field_under_player_is_free =
13173       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13174     boolean player_is_standing_on_valid_field =
13175       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13176        (IS_WALKABLE(Feld[jx][jy]) &&
13177         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13178
13179     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13180       player->programmed_action = MV_DOWN;
13181   }
13182 }
13183
13184 /*
13185   MovePlayerOneStep()
13186   -----------------------------------------------------------------------------
13187   dx, dy:               direction (non-diagonal) to try to move the player to
13188   real_dx, real_dy:     direction as read from input device (can be diagonal)
13189 */
13190
13191 boolean MovePlayerOneStep(struct PlayerInfo *player,
13192                           int dx, int dy, int real_dx, int real_dy)
13193 {
13194   int jx = player->jx, jy = player->jy;
13195   int new_jx = jx + dx, new_jy = jy + dy;
13196 #if !USE_FIXED_DONT_RUN_INTO
13197   int element;
13198 #endif
13199   int can_move;
13200   boolean player_can_move = !player->cannot_move;
13201
13202   if (!player->active || (!dx && !dy))
13203     return MP_NO_ACTION;
13204
13205   player->MovDir = (dx < 0 ? MV_LEFT :
13206                     dx > 0 ? MV_RIGHT :
13207                     dy < 0 ? MV_UP :
13208                     dy > 0 ? MV_DOWN :  MV_NONE);
13209
13210   if (!IN_LEV_FIELD(new_jx, new_jy))
13211     return MP_NO_ACTION;
13212
13213   if (!player_can_move)
13214   {
13215     if (player->MovPos == 0)
13216     {
13217       player->is_moving = FALSE;
13218       player->is_digging = FALSE;
13219       player->is_collecting = FALSE;
13220       player->is_snapping = FALSE;
13221       player->is_pushing = FALSE;
13222     }
13223   }
13224
13225 #if 1
13226   if (!options.network && game.centered_player_nr == -1 &&
13227       !AllPlayersInSight(player, new_jx, new_jy))
13228     return MP_NO_ACTION;
13229 #else
13230   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13231     return MP_NO_ACTION;
13232 #endif
13233
13234 #if !USE_FIXED_DONT_RUN_INTO
13235   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13236
13237   /* (moved to DigField()) */
13238   if (player_can_move && DONT_RUN_INTO(element))
13239   {
13240     if (element == EL_ACID && dx == 0 && dy == 1)
13241     {
13242       SplashAcid(new_jx, new_jy);
13243       Feld[jx][jy] = EL_PLAYER_1;
13244       InitMovingField(jx, jy, MV_DOWN);
13245       Store[jx][jy] = EL_ACID;
13246       ContinueMoving(jx, jy);
13247       BuryPlayer(player);
13248     }
13249     else
13250       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13251
13252     return MP_MOVING;
13253   }
13254 #endif
13255
13256   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13257   if (can_move != MP_MOVING)
13258     return can_move;
13259
13260   /* check if DigField() has caused relocation of the player */
13261   if (player->jx != jx || player->jy != jy)
13262     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13263
13264   StorePlayer[jx][jy] = 0;
13265   player->last_jx = jx;
13266   player->last_jy = jy;
13267   player->jx = new_jx;
13268   player->jy = new_jy;
13269   StorePlayer[new_jx][new_jy] = player->element_nr;
13270
13271   if (player->move_delay_value_next != -1)
13272   {
13273     player->move_delay_value = player->move_delay_value_next;
13274     player->move_delay_value_next = -1;
13275   }
13276
13277   player->MovPos =
13278     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13279
13280   player->step_counter++;
13281
13282   PlayerVisit[jx][jy] = FrameCounter;
13283
13284 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13285   player->is_moving = TRUE;
13286 #endif
13287
13288 #if 1
13289   /* should better be called in MovePlayer(), but this breaks some tapes */
13290   ScrollPlayer(player, SCROLL_INIT);
13291 #endif
13292
13293   return MP_MOVING;
13294 }
13295
13296 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13297 {
13298   int jx = player->jx, jy = player->jy;
13299   int old_jx = jx, old_jy = jy;
13300   int moved = MP_NO_ACTION;
13301
13302   if (!player->active)
13303     return FALSE;
13304
13305   if (!dx && !dy)
13306   {
13307     if (player->MovPos == 0)
13308     {
13309       player->is_moving = FALSE;
13310       player->is_digging = FALSE;
13311       player->is_collecting = FALSE;
13312       player->is_snapping = FALSE;
13313       player->is_pushing = FALSE;
13314     }
13315
13316     return FALSE;
13317   }
13318
13319   if (player->move_delay > 0)
13320     return FALSE;
13321
13322   player->move_delay = -1;              /* set to "uninitialized" value */
13323
13324   /* store if player is automatically moved to next field */
13325   player->is_auto_moving = (player->programmed_action != MV_NONE);
13326
13327   /* remove the last programmed player action */
13328   player->programmed_action = 0;
13329
13330   if (player->MovPos)
13331   {
13332     /* should only happen if pre-1.2 tape recordings are played */
13333     /* this is only for backward compatibility */
13334
13335     int original_move_delay_value = player->move_delay_value;
13336
13337 #if DEBUG
13338     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13339            tape.counter);
13340 #endif
13341
13342     /* scroll remaining steps with finest movement resolution */
13343     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13344
13345     while (player->MovPos)
13346     {
13347       ScrollPlayer(player, SCROLL_GO_ON);
13348       ScrollScreen(NULL, SCROLL_GO_ON);
13349
13350       AdvanceFrameAndPlayerCounters(player->index_nr);
13351
13352       DrawAllPlayers();
13353       BackToFront();
13354     }
13355
13356     player->move_delay_value = original_move_delay_value;
13357   }
13358
13359   player->is_active = FALSE;
13360
13361   if (player->last_move_dir & MV_HORIZONTAL)
13362   {
13363     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13364       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13365   }
13366   else
13367   {
13368     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13369       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13370   }
13371
13372 #if USE_FIXED_BORDER_RUNNING_GFX
13373   if (!moved && !player->is_active)
13374   {
13375     player->is_moving = FALSE;
13376     player->is_digging = FALSE;
13377     player->is_collecting = FALSE;
13378     player->is_snapping = FALSE;
13379     player->is_pushing = FALSE;
13380   }
13381 #endif
13382
13383   jx = player->jx;
13384   jy = player->jy;
13385
13386 #if 1
13387   if (moved & MP_MOVING && !ScreenMovPos &&
13388       (player->index_nr == game.centered_player_nr ||
13389        game.centered_player_nr == -1))
13390 #else
13391   if (moved & MP_MOVING && !ScreenMovPos &&
13392       (player == local_player || !options.network))
13393 #endif
13394   {
13395     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13396     int offset = game.scroll_delay_value;
13397
13398     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13399     {
13400       /* actual player has left the screen -- scroll in that direction */
13401       if (jx != old_jx)         /* player has moved horizontally */
13402         scroll_x += (jx - old_jx);
13403       else                      /* player has moved vertically */
13404         scroll_y += (jy - old_jy);
13405     }
13406     else
13407     {
13408       if (jx != old_jx)         /* player has moved horizontally */
13409       {
13410         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13411             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13412           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13413
13414         /* don't scroll over playfield boundaries */
13415         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13416           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13417
13418         /* don't scroll more than one field at a time */
13419         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13420
13421         /* don't scroll against the player's moving direction */
13422         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13423             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13424           scroll_x = old_scroll_x;
13425       }
13426       else                      /* player has moved vertically */
13427       {
13428         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13429             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13430           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13431
13432         /* don't scroll over playfield boundaries */
13433         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13434           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13435
13436         /* don't scroll more than one field at a time */
13437         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13438
13439         /* don't scroll against the player's moving direction */
13440         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13441             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13442           scroll_y = old_scroll_y;
13443       }
13444     }
13445
13446     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13447     {
13448 #if 1
13449       if (!options.network && game.centered_player_nr == -1 &&
13450           !AllPlayersInVisibleScreen())
13451       {
13452         scroll_x = old_scroll_x;
13453         scroll_y = old_scroll_y;
13454       }
13455       else
13456 #else
13457       if (!options.network && !AllPlayersInVisibleScreen())
13458       {
13459         scroll_x = old_scroll_x;
13460         scroll_y = old_scroll_y;
13461       }
13462       else
13463 #endif
13464       {
13465         ScrollScreen(player, SCROLL_INIT);
13466         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13467       }
13468     }
13469   }
13470
13471   player->StepFrame = 0;
13472
13473   if (moved & MP_MOVING)
13474   {
13475     if (old_jx != jx && old_jy == jy)
13476       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13477     else if (old_jx == jx && old_jy != jy)
13478       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13479
13480     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13481
13482     player->last_move_dir = player->MovDir;
13483     player->is_moving = TRUE;
13484     player->is_snapping = FALSE;
13485     player->is_switching = FALSE;
13486     player->is_dropping = FALSE;
13487     player->is_dropping_pressed = FALSE;
13488     player->drop_pressed_delay = 0;
13489
13490 #if 0
13491     /* should better be called here than above, but this breaks some tapes */
13492     ScrollPlayer(player, SCROLL_INIT);
13493 #endif
13494   }
13495   else
13496   {
13497     CheckGravityMovementWhenNotMoving(player);
13498
13499     player->is_moving = FALSE;
13500
13501     /* at this point, the player is allowed to move, but cannot move right now
13502        (e.g. because of something blocking the way) -- ensure that the player
13503        is also allowed to move in the next frame (in old versions before 3.1.1,
13504        the player was forced to wait again for eight frames before next try) */
13505
13506     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13507       player->move_delay = 0;   /* allow direct movement in the next frame */
13508   }
13509
13510   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13511     player->move_delay = player->move_delay_value;
13512
13513   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13514   {
13515     TestIfPlayerTouchesBadThing(jx, jy);
13516     TestIfPlayerTouchesCustomElement(jx, jy);
13517   }
13518
13519   if (!player->active)
13520     RemovePlayer(player);
13521
13522   return moved;
13523 }
13524
13525 void ScrollPlayer(struct PlayerInfo *player, int mode)
13526 {
13527   int jx = player->jx, jy = player->jy;
13528   int last_jx = player->last_jx, last_jy = player->last_jy;
13529   int move_stepsize = TILEX / player->move_delay_value;
13530
13531 #if USE_NEW_PLAYER_SPEED
13532   if (!player->active)
13533     return;
13534
13535   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13536     return;
13537 #else
13538   if (!player->active || player->MovPos == 0)
13539     return;
13540 #endif
13541
13542   if (mode == SCROLL_INIT)
13543   {
13544     player->actual_frame_counter = FrameCounter;
13545     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13546
13547     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13548         Feld[last_jx][last_jy] == EL_EMPTY)
13549     {
13550       int last_field_block_delay = 0;   /* start with no blocking at all */
13551       int block_delay_adjustment = player->block_delay_adjustment;
13552
13553       /* if player blocks last field, add delay for exactly one move */
13554       if (player->block_last_field)
13555       {
13556         last_field_block_delay += player->move_delay_value;
13557
13558         /* when blocking enabled, prevent moving up despite gravity */
13559 #if USE_PLAYER_GRAVITY
13560         if (player->gravity && player->MovDir == MV_UP)
13561           block_delay_adjustment = -1;
13562 #else
13563         if (game.gravity && player->MovDir == MV_UP)
13564           block_delay_adjustment = -1;
13565 #endif
13566       }
13567
13568       /* add block delay adjustment (also possible when not blocking) */
13569       last_field_block_delay += block_delay_adjustment;
13570
13571       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13572       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13573     }
13574
13575 #if USE_NEW_PLAYER_SPEED
13576     if (player->MovPos != 0)    /* player has not yet reached destination */
13577       return;
13578 #else
13579     return;
13580 #endif
13581   }
13582   else if (!FrameReached(&player->actual_frame_counter, 1))
13583     return;
13584
13585 #if USE_NEW_PLAYER_SPEED
13586   if (player->MovPos != 0)
13587   {
13588     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13589     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13590
13591     /* before DrawPlayer() to draw correct player graphic for this case */
13592     if (player->MovPos == 0)
13593       CheckGravityMovement(player);
13594   }
13595 #else
13596   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13597   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13598
13599   /* before DrawPlayer() to draw correct player graphic for this case */
13600   if (player->MovPos == 0)
13601     CheckGravityMovement(player);
13602 #endif
13603
13604   if (player->MovPos == 0)      /* player reached destination field */
13605   {
13606     if (player->move_delay_reset_counter > 0)
13607     {
13608       player->move_delay_reset_counter--;
13609
13610       if (player->move_delay_reset_counter == 0)
13611       {
13612         /* continue with normal speed after quickly moving through gate */
13613         HALVE_PLAYER_SPEED(player);
13614
13615         /* be able to make the next move without delay */
13616         player->move_delay = 0;
13617       }
13618     }
13619
13620     player->last_jx = jx;
13621     player->last_jy = jy;
13622
13623     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13624         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13625         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13626         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13627         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13628         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13629     {
13630       DrawPlayer(player);       /* needed here only to cleanup last field */
13631       RemovePlayer(player);
13632
13633       if (local_player->friends_still_needed == 0 ||
13634           IS_SP_ELEMENT(Feld[jx][jy]))
13635         PlayerWins(player);
13636     }
13637
13638     /* this breaks one level: "machine", level 000 */
13639     {
13640       int move_direction = player->MovDir;
13641       int enter_side = MV_DIR_OPPOSITE(move_direction);
13642       int leave_side = move_direction;
13643       int old_jx = last_jx;
13644       int old_jy = last_jy;
13645       int old_element = Feld[old_jx][old_jy];
13646       int new_element = Feld[jx][jy];
13647
13648       if (IS_CUSTOM_ELEMENT(old_element))
13649         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13650                                    CE_LEFT_BY_PLAYER,
13651                                    player->index_bit, leave_side);
13652
13653       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13654                                           CE_PLAYER_LEAVES_X,
13655                                           player->index_bit, leave_side);
13656
13657       if (IS_CUSTOM_ELEMENT(new_element))
13658         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13659                                    player->index_bit, enter_side);
13660
13661       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13662                                           CE_PLAYER_ENTERS_X,
13663                                           player->index_bit, enter_side);
13664
13665 #if USE_FIX_CE_ACTION_WITH_PLAYER
13666       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13667                                         CE_MOVE_OF_X, move_direction);
13668 #else
13669       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13670                                         CE_MOVE_OF_X, move_direction);
13671 #endif
13672     }
13673
13674     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13675     {
13676       TestIfPlayerTouchesBadThing(jx, jy);
13677       TestIfPlayerTouchesCustomElement(jx, jy);
13678
13679       /* needed because pushed element has not yet reached its destination,
13680          so it would trigger a change event at its previous field location */
13681       if (!player->is_pushing)
13682         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13683
13684       if (!player->active)
13685         RemovePlayer(player);
13686     }
13687
13688     if (!local_player->LevelSolved && level.use_step_counter)
13689     {
13690       int i;
13691
13692       TimePlayed++;
13693
13694       if (TimeLeft > 0)
13695       {
13696         TimeLeft--;
13697
13698         if (TimeLeft <= 10 && setup.time_limit)
13699           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13700
13701 #if 1
13702         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13703
13704         DisplayGameControlValues();
13705 #else
13706         DrawGameValue_Time(TimeLeft);
13707 #endif
13708
13709         if (!TimeLeft && setup.time_limit)
13710           for (i = 0; i < MAX_PLAYERS; i++)
13711             KillPlayer(&stored_player[i]);
13712       }
13713 #if 1
13714       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13715       {
13716         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13717
13718         DisplayGameControlValues();
13719       }
13720 #else
13721       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13722         DrawGameValue_Time(TimePlayed);
13723 #endif
13724     }
13725
13726     if (tape.single_step && tape.recording && !tape.pausing &&
13727         !player->programmed_action)
13728       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13729   }
13730 }
13731
13732 void ScrollScreen(struct PlayerInfo *player, int mode)
13733 {
13734   static unsigned long screen_frame_counter = 0;
13735
13736   if (mode == SCROLL_INIT)
13737   {
13738     /* set scrolling step size according to actual player's moving speed */
13739     ScrollStepSize = TILEX / player->move_delay_value;
13740
13741     screen_frame_counter = FrameCounter;
13742     ScreenMovDir = player->MovDir;
13743     ScreenMovPos = player->MovPos;
13744     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13745     return;
13746   }
13747   else if (!FrameReached(&screen_frame_counter, 1))
13748     return;
13749
13750   if (ScreenMovPos)
13751   {
13752     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13753     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13754     redraw_mask |= REDRAW_FIELD;
13755   }
13756   else
13757     ScreenMovDir = MV_NONE;
13758 }
13759
13760 void TestIfPlayerTouchesCustomElement(int x, int y)
13761 {
13762   static int xy[4][2] =
13763   {
13764     { 0, -1 },
13765     { -1, 0 },
13766     { +1, 0 },
13767     { 0, +1 }
13768   };
13769   static int trigger_sides[4][2] =
13770   {
13771     /* center side       border side */
13772     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13773     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13774     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13775     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13776   };
13777   static int touch_dir[4] =
13778   {
13779     MV_LEFT | MV_RIGHT,
13780     MV_UP   | MV_DOWN,
13781     MV_UP   | MV_DOWN,
13782     MV_LEFT | MV_RIGHT
13783   };
13784   int center_element = Feld[x][y];      /* should always be non-moving! */
13785   int i;
13786
13787   for (i = 0; i < NUM_DIRECTIONS; i++)
13788   {
13789     int xx = x + xy[i][0];
13790     int yy = y + xy[i][1];
13791     int center_side = trigger_sides[i][0];
13792     int border_side = trigger_sides[i][1];
13793     int border_element;
13794
13795     if (!IN_LEV_FIELD(xx, yy))
13796       continue;
13797
13798     if (IS_PLAYER(x, y))                /* player found at center element */
13799     {
13800       struct PlayerInfo *player = PLAYERINFO(x, y);
13801
13802       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13803         border_element = Feld[xx][yy];          /* may be moving! */
13804       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13805         border_element = Feld[xx][yy];
13806       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13807         border_element = MovingOrBlocked2Element(xx, yy);
13808       else
13809         continue;               /* center and border element do not touch */
13810
13811       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13812                                  player->index_bit, border_side);
13813       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13814                                           CE_PLAYER_TOUCHES_X,
13815                                           player->index_bit, border_side);
13816
13817 #if USE_FIX_CE_ACTION_WITH_PLAYER
13818       {
13819         /* use player element that is initially defined in the level playfield,
13820            not the player element that corresponds to the runtime player number
13821            (example: a level that contains EL_PLAYER_3 as the only player would
13822            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13823         int player_element = PLAYERINFO(x, y)->initial_element;
13824
13825         CheckElementChangeBySide(xx, yy, border_element, player_element,
13826                                  CE_TOUCHING_X, border_side);
13827       }
13828 #endif
13829     }
13830     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13831     {
13832       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13833
13834       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13835       {
13836         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13837           continue;             /* center and border element do not touch */
13838       }
13839
13840       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13841                                  player->index_bit, center_side);
13842       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13843                                           CE_PLAYER_TOUCHES_X,
13844                                           player->index_bit, center_side);
13845
13846 #if USE_FIX_CE_ACTION_WITH_PLAYER
13847       {
13848         /* use player element that is initially defined in the level playfield,
13849            not the player element that corresponds to the runtime player number
13850            (example: a level that contains EL_PLAYER_3 as the only player would
13851            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13852         int player_element = PLAYERINFO(xx, yy)->initial_element;
13853
13854         CheckElementChangeBySide(x, y, center_element, player_element,
13855                                  CE_TOUCHING_X, center_side);
13856       }
13857 #endif
13858
13859       break;
13860     }
13861   }
13862 }
13863
13864 #if USE_ELEMENT_TOUCHING_BUGFIX
13865
13866 void TestIfElementTouchesCustomElement(int x, int y)
13867 {
13868   static int xy[4][2] =
13869   {
13870     { 0, -1 },
13871     { -1, 0 },
13872     { +1, 0 },
13873     { 0, +1 }
13874   };
13875   static int trigger_sides[4][2] =
13876   {
13877     /* center side      border side */
13878     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13879     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13880     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13881     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13882   };
13883   static int touch_dir[4] =
13884   {
13885     MV_LEFT | MV_RIGHT,
13886     MV_UP   | MV_DOWN,
13887     MV_UP   | MV_DOWN,
13888     MV_LEFT | MV_RIGHT
13889   };
13890   boolean change_center_element = FALSE;
13891   int center_element = Feld[x][y];      /* should always be non-moving! */
13892   int border_element_old[NUM_DIRECTIONS];
13893   int i;
13894
13895   for (i = 0; i < NUM_DIRECTIONS; i++)
13896   {
13897     int xx = x + xy[i][0];
13898     int yy = y + xy[i][1];
13899     int border_element;
13900
13901     border_element_old[i] = -1;
13902
13903     if (!IN_LEV_FIELD(xx, yy))
13904       continue;
13905
13906     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13907       border_element = Feld[xx][yy];    /* may be moving! */
13908     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13909       border_element = Feld[xx][yy];
13910     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13911       border_element = MovingOrBlocked2Element(xx, yy);
13912     else
13913       continue;                 /* center and border element do not touch */
13914
13915     border_element_old[i] = border_element;
13916   }
13917
13918   for (i = 0; i < NUM_DIRECTIONS; i++)
13919   {
13920     int xx = x + xy[i][0];
13921     int yy = y + xy[i][1];
13922     int center_side = trigger_sides[i][0];
13923     int border_element = border_element_old[i];
13924
13925     if (border_element == -1)
13926       continue;
13927
13928     /* check for change of border element */
13929     CheckElementChangeBySide(xx, yy, border_element, center_element,
13930                              CE_TOUCHING_X, center_side);
13931
13932     /* (center element cannot be player, so we dont have to check this here) */
13933   }
13934
13935   for (i = 0; i < NUM_DIRECTIONS; i++)
13936   {
13937     int xx = x + xy[i][0];
13938     int yy = y + xy[i][1];
13939     int border_side = trigger_sides[i][1];
13940     int border_element = border_element_old[i];
13941
13942     if (border_element == -1)
13943       continue;
13944
13945     /* check for change of center element (but change it only once) */
13946     if (!change_center_element)
13947       change_center_element =
13948         CheckElementChangeBySide(x, y, center_element, border_element,
13949                                  CE_TOUCHING_X, border_side);
13950
13951 #if USE_FIX_CE_ACTION_WITH_PLAYER
13952     if (IS_PLAYER(xx, yy))
13953     {
13954       /* use player element that is initially defined in the level playfield,
13955          not the player element that corresponds to the runtime player number
13956          (example: a level that contains EL_PLAYER_3 as the only player would
13957          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13958       int player_element = PLAYERINFO(xx, yy)->initial_element;
13959
13960       CheckElementChangeBySide(x, y, center_element, player_element,
13961                                CE_TOUCHING_X, border_side);
13962     }
13963 #endif
13964   }
13965 }
13966
13967 #else
13968
13969 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13970 {
13971   static int xy[4][2] =
13972   {
13973     { 0, -1 },
13974     { -1, 0 },
13975     { +1, 0 },
13976     { 0, +1 }
13977   };
13978   static int trigger_sides[4][2] =
13979   {
13980     /* center side      border side */
13981     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13982     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13983     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13984     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13985   };
13986   static int touch_dir[4] =
13987   {
13988     MV_LEFT | MV_RIGHT,
13989     MV_UP   | MV_DOWN,
13990     MV_UP   | MV_DOWN,
13991     MV_LEFT | MV_RIGHT
13992   };
13993   boolean change_center_element = FALSE;
13994   int center_element = Feld[x][y];      /* should always be non-moving! */
13995   int i;
13996
13997   for (i = 0; i < NUM_DIRECTIONS; i++)
13998   {
13999     int xx = x + xy[i][0];
14000     int yy = y + xy[i][1];
14001     int center_side = trigger_sides[i][0];
14002     int border_side = trigger_sides[i][1];
14003     int border_element;
14004
14005     if (!IN_LEV_FIELD(xx, yy))
14006       continue;
14007
14008     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14009       border_element = Feld[xx][yy];    /* may be moving! */
14010     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14011       border_element = Feld[xx][yy];
14012     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14013       border_element = MovingOrBlocked2Element(xx, yy);
14014     else
14015       continue;                 /* center and border element do not touch */
14016
14017     /* check for change of center element (but change it only once) */
14018     if (!change_center_element)
14019       change_center_element =
14020         CheckElementChangeBySide(x, y, center_element, border_element,
14021                                  CE_TOUCHING_X, border_side);
14022
14023     /* check for change of border element */
14024     CheckElementChangeBySide(xx, yy, border_element, center_element,
14025                              CE_TOUCHING_X, center_side);
14026   }
14027 }
14028
14029 #endif
14030
14031 void TestIfElementHitsCustomElement(int x, int y, int direction)
14032 {
14033   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14034   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14035   int hitx = x + dx, hity = y + dy;
14036   int hitting_element = Feld[x][y];
14037   int touched_element;
14038
14039   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14040     return;
14041
14042   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14043                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14044
14045   if (IN_LEV_FIELD(hitx, hity))
14046   {
14047     int opposite_direction = MV_DIR_OPPOSITE(direction);
14048     int hitting_side = direction;
14049     int touched_side = opposite_direction;
14050     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14051                           MovDir[hitx][hity] != direction ||
14052                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14053
14054     object_hit = TRUE;
14055
14056     if (object_hit)
14057     {
14058       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14059                                CE_HITTING_X, touched_side);
14060
14061       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14062                                CE_HIT_BY_X, hitting_side);
14063
14064       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14065                                CE_HIT_BY_SOMETHING, opposite_direction);
14066
14067 #if USE_FIX_CE_ACTION_WITH_PLAYER
14068       if (IS_PLAYER(hitx, hity))
14069       {
14070         /* use player element that is initially defined in the level playfield,
14071            not the player element that corresponds to the runtime player number
14072            (example: a level that contains EL_PLAYER_3 as the only player would
14073            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14074         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14075
14076         CheckElementChangeBySide(x, y, hitting_element, player_element,
14077                                  CE_HITTING_X, touched_side);
14078       }
14079 #endif
14080     }
14081   }
14082
14083   /* "hitting something" is also true when hitting the playfield border */
14084   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14085                            CE_HITTING_SOMETHING, direction);
14086 }
14087
14088 #if 0
14089 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14090 {
14091   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14092   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14093   int hitx = x + dx, hity = y + dy;
14094   int hitting_element = Feld[x][y];
14095   int touched_element;
14096 #if 0
14097   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14098                         !IS_FREE(hitx, hity) &&
14099                         (!IS_MOVING(hitx, hity) ||
14100                          MovDir[hitx][hity] != direction ||
14101                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14102 #endif
14103
14104   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14105     return;
14106
14107 #if 0
14108   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14109     return;
14110 #endif
14111
14112   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14113                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14114
14115   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14116                            EP_CAN_SMASH_EVERYTHING, direction);
14117
14118   if (IN_LEV_FIELD(hitx, hity))
14119   {
14120     int opposite_direction = MV_DIR_OPPOSITE(direction);
14121     int hitting_side = direction;
14122     int touched_side = opposite_direction;
14123 #if 0
14124     int touched_element = MovingOrBlocked2Element(hitx, hity);
14125 #endif
14126 #if 1
14127     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14128                           MovDir[hitx][hity] != direction ||
14129                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14130
14131     object_hit = TRUE;
14132 #endif
14133
14134     if (object_hit)
14135     {
14136       int i;
14137
14138       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14139                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14140
14141       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14142                                CE_OTHER_IS_SMASHING, touched_side);
14143
14144       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14145                                CE_OTHER_GETS_SMASHED, hitting_side);
14146     }
14147   }
14148 }
14149 #endif
14150
14151 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14152 {
14153   int i, kill_x = -1, kill_y = -1;
14154
14155   int bad_element = -1;
14156   static int test_xy[4][2] =
14157   {
14158     { 0, -1 },
14159     { -1, 0 },
14160     { +1, 0 },
14161     { 0, +1 }
14162   };
14163   static int test_dir[4] =
14164   {
14165     MV_UP,
14166     MV_LEFT,
14167     MV_RIGHT,
14168     MV_DOWN
14169   };
14170
14171   for (i = 0; i < NUM_DIRECTIONS; i++)
14172   {
14173     int test_x, test_y, test_move_dir, test_element;
14174
14175     test_x = good_x + test_xy[i][0];
14176     test_y = good_y + test_xy[i][1];
14177
14178     if (!IN_LEV_FIELD(test_x, test_y))
14179       continue;
14180
14181     test_move_dir =
14182       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14183
14184     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14185
14186     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14187        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14188     */
14189     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14190         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14191     {
14192       kill_x = test_x;
14193       kill_y = test_y;
14194       bad_element = test_element;
14195
14196       break;
14197     }
14198   }
14199
14200   if (kill_x != -1 || kill_y != -1)
14201   {
14202     if (IS_PLAYER(good_x, good_y))
14203     {
14204       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14205
14206       if (player->shield_deadly_time_left > 0 &&
14207           !IS_INDESTRUCTIBLE(bad_element))
14208         Bang(kill_x, kill_y);
14209       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14210         KillPlayer(player);
14211     }
14212     else
14213       Bang(good_x, good_y);
14214   }
14215 }
14216
14217 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14218 {
14219   int i, kill_x = -1, kill_y = -1;
14220   int bad_element = Feld[bad_x][bad_y];
14221   static int test_xy[4][2] =
14222   {
14223     { 0, -1 },
14224     { -1, 0 },
14225     { +1, 0 },
14226     { 0, +1 }
14227   };
14228   static int touch_dir[4] =
14229   {
14230     MV_LEFT | MV_RIGHT,
14231     MV_UP   | MV_DOWN,
14232     MV_UP   | MV_DOWN,
14233     MV_LEFT | MV_RIGHT
14234   };
14235   static int test_dir[4] =
14236   {
14237     MV_UP,
14238     MV_LEFT,
14239     MV_RIGHT,
14240     MV_DOWN
14241   };
14242
14243   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14244     return;
14245
14246   for (i = 0; i < NUM_DIRECTIONS; i++)
14247   {
14248     int test_x, test_y, test_move_dir, test_element;
14249
14250     test_x = bad_x + test_xy[i][0];
14251     test_y = bad_y + test_xy[i][1];
14252
14253     if (!IN_LEV_FIELD(test_x, test_y))
14254       continue;
14255
14256     test_move_dir =
14257       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14258
14259     test_element = Feld[test_x][test_y];
14260
14261     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14262        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14263     */
14264     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14265         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14266     {
14267       /* good thing is player or penguin that does not move away */
14268       if (IS_PLAYER(test_x, test_y))
14269       {
14270         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14271
14272         if (bad_element == EL_ROBOT && player->is_moving)
14273           continue;     /* robot does not kill player if he is moving */
14274
14275         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14276         {
14277           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14278             continue;           /* center and border element do not touch */
14279         }
14280
14281         kill_x = test_x;
14282         kill_y = test_y;
14283
14284         break;
14285       }
14286       else if (test_element == EL_PENGUIN)
14287       {
14288         kill_x = test_x;
14289         kill_y = test_y;
14290
14291         break;
14292       }
14293     }
14294   }
14295
14296   if (kill_x != -1 || kill_y != -1)
14297   {
14298     if (IS_PLAYER(kill_x, kill_y))
14299     {
14300       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14301
14302       if (player->shield_deadly_time_left > 0 &&
14303           !IS_INDESTRUCTIBLE(bad_element))
14304         Bang(bad_x, bad_y);
14305       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14306         KillPlayer(player);
14307     }
14308     else
14309       Bang(kill_x, kill_y);
14310   }
14311 }
14312
14313 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14314 {
14315   int bad_element = Feld[bad_x][bad_y];
14316   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14317   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14318   int test_x = bad_x + dx, test_y = bad_y + dy;
14319   int test_move_dir, test_element;
14320   int kill_x = -1, kill_y = -1;
14321
14322   if (!IN_LEV_FIELD(test_x, test_y))
14323     return;
14324
14325   test_move_dir =
14326     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14327
14328   test_element = Feld[test_x][test_y];
14329
14330   if (test_move_dir != bad_move_dir)
14331   {
14332     /* good thing can be player or penguin that does not move away */
14333     if (IS_PLAYER(test_x, test_y))
14334     {
14335       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14336
14337       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14338          player as being hit when he is moving towards the bad thing, because
14339          the "get hit by" condition would be lost after the player stops) */
14340       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14341         return;         /* player moves away from bad thing */
14342
14343       kill_x = test_x;
14344       kill_y = test_y;
14345     }
14346     else if (test_element == EL_PENGUIN)
14347     {
14348       kill_x = test_x;
14349       kill_y = test_y;
14350     }
14351   }
14352
14353   if (kill_x != -1 || kill_y != -1)
14354   {
14355     if (IS_PLAYER(kill_x, kill_y))
14356     {
14357       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14358
14359       if (player->shield_deadly_time_left > 0 &&
14360           !IS_INDESTRUCTIBLE(bad_element))
14361         Bang(bad_x, bad_y);
14362       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14363         KillPlayer(player);
14364     }
14365     else
14366       Bang(kill_x, kill_y);
14367   }
14368 }
14369
14370 void TestIfPlayerTouchesBadThing(int x, int y)
14371 {
14372   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14373 }
14374
14375 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14376 {
14377   TestIfGoodThingHitsBadThing(x, y, move_dir);
14378 }
14379
14380 void TestIfBadThingTouchesPlayer(int x, int y)
14381 {
14382   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14383 }
14384
14385 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14386 {
14387   TestIfBadThingHitsGoodThing(x, y, move_dir);
14388 }
14389
14390 void TestIfFriendTouchesBadThing(int x, int y)
14391 {
14392   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14393 }
14394
14395 void TestIfBadThingTouchesFriend(int x, int y)
14396 {
14397   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14398 }
14399
14400 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14401 {
14402   int i, kill_x = bad_x, kill_y = bad_y;
14403   static int xy[4][2] =
14404   {
14405     { 0, -1 },
14406     { -1, 0 },
14407     { +1, 0 },
14408     { 0, +1 }
14409   };
14410
14411   for (i = 0; i < NUM_DIRECTIONS; i++)
14412   {
14413     int x, y, element;
14414
14415     x = bad_x + xy[i][0];
14416     y = bad_y + xy[i][1];
14417     if (!IN_LEV_FIELD(x, y))
14418       continue;
14419
14420     element = Feld[x][y];
14421     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14422         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14423     {
14424       kill_x = x;
14425       kill_y = y;
14426       break;
14427     }
14428   }
14429
14430   if (kill_x != bad_x || kill_y != bad_y)
14431     Bang(bad_x, bad_y);
14432 }
14433
14434 void KillPlayer(struct PlayerInfo *player)
14435 {
14436   int jx = player->jx, jy = player->jy;
14437
14438   if (!player->active)
14439     return;
14440
14441 #if 0
14442   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14443          player->killed, player->active, player->reanimated);
14444 #endif
14445
14446   /* the following code was introduced to prevent an infinite loop when calling
14447      -> Bang()
14448      -> CheckTriggeredElementChangeExt()
14449      -> ExecuteCustomElementAction()
14450      -> KillPlayer()
14451      -> (infinitely repeating the above sequence of function calls)
14452      which occurs when killing the player while having a CE with the setting
14453      "kill player X when explosion of <player X>"; the solution using a new
14454      field "player->killed" was chosen for backwards compatibility, although
14455      clever use of the fields "player->active" etc. would probably also work */
14456 #if 1
14457   if (player->killed)
14458     return;
14459 #endif
14460
14461   player->killed = TRUE;
14462
14463   /* remove accessible field at the player's position */
14464   Feld[jx][jy] = EL_EMPTY;
14465
14466   /* deactivate shield (else Bang()/Explode() would not work right) */
14467   player->shield_normal_time_left = 0;
14468   player->shield_deadly_time_left = 0;
14469
14470 #if 0
14471   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14472          player->killed, player->active, player->reanimated);
14473 #endif
14474
14475   Bang(jx, jy);
14476
14477 #if 0
14478   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14479          player->killed, player->active, player->reanimated);
14480 #endif
14481
14482 #if USE_PLAYER_REANIMATION
14483 #if 1
14484   if (player->reanimated)       /* killed player may have been reanimated */
14485     player->killed = player->reanimated = FALSE;
14486   else
14487     BuryPlayer(player);
14488 #else
14489   if (player->killed)           /* player may have been reanimated */
14490     BuryPlayer(player);
14491 #endif
14492 #else
14493   BuryPlayer(player);
14494 #endif
14495 }
14496
14497 static void KillPlayerUnlessEnemyProtected(int x, int y)
14498 {
14499   if (!PLAYER_ENEMY_PROTECTED(x, y))
14500     KillPlayer(PLAYERINFO(x, y));
14501 }
14502
14503 static void KillPlayerUnlessExplosionProtected(int x, int y)
14504 {
14505   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14506     KillPlayer(PLAYERINFO(x, y));
14507 }
14508
14509 void BuryPlayer(struct PlayerInfo *player)
14510 {
14511   int jx = player->jx, jy = player->jy;
14512
14513   if (!player->active)
14514     return;
14515
14516   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14517   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14518
14519   player->GameOver = TRUE;
14520   RemovePlayer(player);
14521 }
14522
14523 void RemovePlayer(struct PlayerInfo *player)
14524 {
14525   int jx = player->jx, jy = player->jy;
14526   int i, found = FALSE;
14527
14528   player->present = FALSE;
14529   player->active = FALSE;
14530
14531   if (!ExplodeField[jx][jy])
14532     StorePlayer[jx][jy] = 0;
14533
14534   if (player->is_moving)
14535     TEST_DrawLevelField(player->last_jx, player->last_jy);
14536
14537   for (i = 0; i < MAX_PLAYERS; i++)
14538     if (stored_player[i].active)
14539       found = TRUE;
14540
14541   if (!found)
14542     AllPlayersGone = TRUE;
14543
14544   ExitX = ZX = jx;
14545   ExitY = ZY = jy;
14546 }
14547
14548 #if USE_NEW_SNAP_DELAY
14549 static void setFieldForSnapping(int x, int y, int element, int direction)
14550 {
14551   struct ElementInfo *ei = &element_info[element];
14552   int direction_bit = MV_DIR_TO_BIT(direction);
14553   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14554   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14555                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14556
14557   Feld[x][y] = EL_ELEMENT_SNAPPING;
14558   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14559
14560   ResetGfxAnimation(x, y);
14561
14562   GfxElement[x][y] = element;
14563   GfxAction[x][y] = action;
14564   GfxDir[x][y] = direction;
14565   GfxFrame[x][y] = -1;
14566 }
14567 #endif
14568
14569 /*
14570   =============================================================================
14571   checkDiagonalPushing()
14572   -----------------------------------------------------------------------------
14573   check if diagonal input device direction results in pushing of object
14574   (by checking if the alternative direction is walkable, diggable, ...)
14575   =============================================================================
14576 */
14577
14578 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14579                                     int x, int y, int real_dx, int real_dy)
14580 {
14581   int jx, jy, dx, dy, xx, yy;
14582
14583   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14584     return TRUE;
14585
14586   /* diagonal direction: check alternative direction */
14587   jx = player->jx;
14588   jy = player->jy;
14589   dx = x - jx;
14590   dy = y - jy;
14591   xx = jx + (dx == 0 ? real_dx : 0);
14592   yy = jy + (dy == 0 ? real_dy : 0);
14593
14594   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14595 }
14596
14597 /*
14598   =============================================================================
14599   DigField()
14600   -----------------------------------------------------------------------------
14601   x, y:                 field next to player (non-diagonal) to try to dig to
14602   real_dx, real_dy:     direction as read from input device (can be diagonal)
14603   =============================================================================
14604 */
14605
14606 static int DigField(struct PlayerInfo *player,
14607                     int oldx, int oldy, int x, int y,
14608                     int real_dx, int real_dy, int mode)
14609 {
14610   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14611   boolean player_was_pushing = player->is_pushing;
14612   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14613   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14614   int jx = oldx, jy = oldy;
14615   int dx = x - jx, dy = y - jy;
14616   int nextx = x + dx, nexty = y + dy;
14617   int move_direction = (dx == -1 ? MV_LEFT  :
14618                         dx == +1 ? MV_RIGHT :
14619                         dy == -1 ? MV_UP    :
14620                         dy == +1 ? MV_DOWN  : MV_NONE);
14621   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14622   int dig_side = MV_DIR_OPPOSITE(move_direction);
14623   int old_element = Feld[jx][jy];
14624 #if USE_FIXED_DONT_RUN_INTO
14625   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14626 #else
14627   int element;
14628 #endif
14629   int collect_count;
14630
14631   if (is_player)                /* function can also be called by EL_PENGUIN */
14632   {
14633     if (player->MovPos == 0)
14634     {
14635       player->is_digging = FALSE;
14636       player->is_collecting = FALSE;
14637     }
14638
14639     if (player->MovPos == 0)    /* last pushing move finished */
14640       player->is_pushing = FALSE;
14641
14642     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14643     {
14644       player->is_switching = FALSE;
14645       player->push_delay = -1;
14646
14647       return MP_NO_ACTION;
14648     }
14649   }
14650
14651 #if !USE_FIXED_DONT_RUN_INTO
14652   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14653     return MP_NO_ACTION;
14654 #endif
14655
14656   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14657     old_element = Back[jx][jy];
14658
14659   /* in case of element dropped at player position, check background */
14660   else if (Back[jx][jy] != EL_EMPTY &&
14661            game.engine_version >= VERSION_IDENT(2,2,0,0))
14662     old_element = Back[jx][jy];
14663
14664   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14665     return MP_NO_ACTION;        /* field has no opening in this direction */
14666
14667   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14668     return MP_NO_ACTION;        /* field has no opening in this direction */
14669
14670 #if USE_FIXED_DONT_RUN_INTO
14671   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14672   {
14673     SplashAcid(x, y);
14674
14675     Feld[jx][jy] = player->artwork_element;
14676     InitMovingField(jx, jy, MV_DOWN);
14677     Store[jx][jy] = EL_ACID;
14678     ContinueMoving(jx, jy);
14679     BuryPlayer(player);
14680
14681     return MP_DONT_RUN_INTO;
14682   }
14683 #endif
14684
14685 #if USE_FIXED_DONT_RUN_INTO
14686   if (player_can_move && DONT_RUN_INTO(element))
14687   {
14688     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14689
14690     return MP_DONT_RUN_INTO;
14691   }
14692 #endif
14693
14694 #if USE_FIXED_DONT_RUN_INTO
14695   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14696     return MP_NO_ACTION;
14697 #endif
14698
14699 #if !USE_FIXED_DONT_RUN_INTO
14700   element = Feld[x][y];
14701 #endif
14702
14703   collect_count = element_info[element].collect_count_initial;
14704
14705   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14706     return MP_NO_ACTION;
14707
14708   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14709     player_can_move = player_can_move_or_snap;
14710
14711   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14712       game.engine_version >= VERSION_IDENT(2,2,0,0))
14713   {
14714     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14715                                player->index_bit, dig_side);
14716     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14717                                         player->index_bit, dig_side);
14718
14719     if (element == EL_DC_LANDMINE)
14720       Bang(x, y);
14721
14722     if (Feld[x][y] != element)          /* field changed by snapping */
14723       return MP_ACTION;
14724
14725     return MP_NO_ACTION;
14726   }
14727
14728 #if USE_PLAYER_GRAVITY
14729   if (player->gravity && is_player && !player->is_auto_moving &&
14730       canFallDown(player) && move_direction != MV_DOWN &&
14731       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14732     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14733 #else
14734   if (game.gravity && is_player && !player->is_auto_moving &&
14735       canFallDown(player) && move_direction != MV_DOWN &&
14736       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14737     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14738 #endif
14739
14740   if (player_can_move &&
14741       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14742   {
14743     int sound_element = SND_ELEMENT(element);
14744     int sound_action = ACTION_WALKING;
14745
14746     if (IS_RND_GATE(element))
14747     {
14748       if (!player->key[RND_GATE_NR(element)])
14749         return MP_NO_ACTION;
14750     }
14751     else if (IS_RND_GATE_GRAY(element))
14752     {
14753       if (!player->key[RND_GATE_GRAY_NR(element)])
14754         return MP_NO_ACTION;
14755     }
14756     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14757     {
14758       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14759         return MP_NO_ACTION;
14760     }
14761     else if (element == EL_EXIT_OPEN ||
14762              element == EL_EM_EXIT_OPEN ||
14763              element == EL_STEEL_EXIT_OPEN ||
14764              element == EL_EM_STEEL_EXIT_OPEN ||
14765              element == EL_SP_EXIT_OPEN ||
14766              element == EL_SP_EXIT_OPENING)
14767     {
14768       sound_action = ACTION_PASSING;    /* player is passing exit */
14769     }
14770     else if (element == EL_EMPTY)
14771     {
14772       sound_action = ACTION_MOVING;             /* nothing to walk on */
14773     }
14774
14775     /* play sound from background or player, whatever is available */
14776     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14777       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14778     else
14779       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14780   }
14781   else if (player_can_move &&
14782            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14783   {
14784     if (!ACCESS_FROM(element, opposite_direction))
14785       return MP_NO_ACTION;      /* field not accessible from this direction */
14786
14787     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14788       return MP_NO_ACTION;
14789
14790     if (IS_EM_GATE(element))
14791     {
14792       if (!player->key[EM_GATE_NR(element)])
14793         return MP_NO_ACTION;
14794     }
14795     else if (IS_EM_GATE_GRAY(element))
14796     {
14797       if (!player->key[EM_GATE_GRAY_NR(element)])
14798         return MP_NO_ACTION;
14799     }
14800     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14801     {
14802       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14803         return MP_NO_ACTION;
14804     }
14805     else if (IS_EMC_GATE(element))
14806     {
14807       if (!player->key[EMC_GATE_NR(element)])
14808         return MP_NO_ACTION;
14809     }
14810     else if (IS_EMC_GATE_GRAY(element))
14811     {
14812       if (!player->key[EMC_GATE_GRAY_NR(element)])
14813         return MP_NO_ACTION;
14814     }
14815     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14816     {
14817       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14818         return MP_NO_ACTION;
14819     }
14820     else if (element == EL_DC_GATE_WHITE ||
14821              element == EL_DC_GATE_WHITE_GRAY ||
14822              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14823     {
14824       if (player->num_white_keys == 0)
14825         return MP_NO_ACTION;
14826
14827       player->num_white_keys--;
14828     }
14829     else if (IS_SP_PORT(element))
14830     {
14831       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14832           element == EL_SP_GRAVITY_PORT_RIGHT ||
14833           element == EL_SP_GRAVITY_PORT_UP ||
14834           element == EL_SP_GRAVITY_PORT_DOWN)
14835 #if USE_PLAYER_GRAVITY
14836         player->gravity = !player->gravity;
14837 #else
14838         game.gravity = !game.gravity;
14839 #endif
14840       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14841                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14842                element == EL_SP_GRAVITY_ON_PORT_UP ||
14843                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14844 #if USE_PLAYER_GRAVITY
14845         player->gravity = TRUE;
14846 #else
14847         game.gravity = TRUE;
14848 #endif
14849       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14850                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14851                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14852                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14853 #if USE_PLAYER_GRAVITY
14854         player->gravity = FALSE;
14855 #else
14856         game.gravity = FALSE;
14857 #endif
14858     }
14859
14860     /* automatically move to the next field with double speed */
14861     player->programmed_action = move_direction;
14862
14863     if (player->move_delay_reset_counter == 0)
14864     {
14865       player->move_delay_reset_counter = 2;     /* two double speed steps */
14866
14867       DOUBLE_PLAYER_SPEED(player);
14868     }
14869
14870     PlayLevelSoundAction(x, y, ACTION_PASSING);
14871   }
14872   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14873   {
14874     RemoveField(x, y);
14875
14876     if (mode != DF_SNAP)
14877     {
14878       GfxElement[x][y] = GFX_ELEMENT(element);
14879       player->is_digging = TRUE;
14880     }
14881
14882     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14883
14884     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14885                                         player->index_bit, dig_side);
14886
14887     if (mode == DF_SNAP)
14888     {
14889 #if USE_NEW_SNAP_DELAY
14890       if (level.block_snap_field)
14891         setFieldForSnapping(x, y, element, move_direction);
14892       else
14893         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14894 #else
14895       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14896 #endif
14897
14898       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14899                                           player->index_bit, dig_side);
14900     }
14901   }
14902   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14903   {
14904     RemoveField(x, y);
14905
14906     if (is_player && mode != DF_SNAP)
14907     {
14908       GfxElement[x][y] = element;
14909       player->is_collecting = TRUE;
14910     }
14911
14912     if (element == EL_SPEED_PILL)
14913     {
14914       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14915     }
14916     else if (element == EL_EXTRA_TIME && level.time > 0)
14917     {
14918       TimeLeft += level.extra_time;
14919
14920 #if 1
14921       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14922
14923       DisplayGameControlValues();
14924 #else
14925       DrawGameValue_Time(TimeLeft);
14926 #endif
14927     }
14928     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14929     {
14930       player->shield_normal_time_left += level.shield_normal_time;
14931       if (element == EL_SHIELD_DEADLY)
14932         player->shield_deadly_time_left += level.shield_deadly_time;
14933     }
14934     else if (element == EL_DYNAMITE ||
14935              element == EL_EM_DYNAMITE ||
14936              element == EL_SP_DISK_RED)
14937     {
14938       if (player->inventory_size < MAX_INVENTORY_SIZE)
14939         player->inventory_element[player->inventory_size++] = element;
14940
14941       DrawGameDoorValues();
14942     }
14943     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14944     {
14945       player->dynabomb_count++;
14946       player->dynabombs_left++;
14947     }
14948     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14949     {
14950       player->dynabomb_size++;
14951     }
14952     else if (element == EL_DYNABOMB_INCREASE_POWER)
14953     {
14954       player->dynabomb_xl = TRUE;
14955     }
14956     else if (IS_KEY(element))
14957     {
14958       player->key[KEY_NR(element)] = TRUE;
14959
14960       DrawGameDoorValues();
14961     }
14962     else if (element == EL_DC_KEY_WHITE)
14963     {
14964       player->num_white_keys++;
14965
14966       /* display white keys? */
14967       /* DrawGameDoorValues(); */
14968     }
14969     else if (IS_ENVELOPE(element))
14970     {
14971       player->show_envelope = element;
14972     }
14973     else if (element == EL_EMC_LENSES)
14974     {
14975       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14976
14977       RedrawAllInvisibleElementsForLenses();
14978     }
14979     else if (element == EL_EMC_MAGNIFIER)
14980     {
14981       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14982
14983       RedrawAllInvisibleElementsForMagnifier();
14984     }
14985     else if (IS_DROPPABLE(element) ||
14986              IS_THROWABLE(element))     /* can be collected and dropped */
14987     {
14988       int i;
14989
14990       if (collect_count == 0)
14991         player->inventory_infinite_element = element;
14992       else
14993         for (i = 0; i < collect_count; i++)
14994           if (player->inventory_size < MAX_INVENTORY_SIZE)
14995             player->inventory_element[player->inventory_size++] = element;
14996
14997       DrawGameDoorValues();
14998     }
14999     else if (collect_count > 0)
15000     {
15001       local_player->gems_still_needed -= collect_count;
15002       if (local_player->gems_still_needed < 0)
15003         local_player->gems_still_needed = 0;
15004
15005 #if 1
15006       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15007
15008       DisplayGameControlValues();
15009 #else
15010       DrawGameValue_Emeralds(local_player->gems_still_needed);
15011 #endif
15012     }
15013
15014     RaiseScoreElement(element);
15015     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15016
15017     if (is_player)
15018       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15019                                           player->index_bit, dig_side);
15020
15021     if (mode == DF_SNAP)
15022     {
15023 #if USE_NEW_SNAP_DELAY
15024       if (level.block_snap_field)
15025         setFieldForSnapping(x, y, element, move_direction);
15026       else
15027         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15028 #else
15029       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15030 #endif
15031
15032       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15033                                           player->index_bit, dig_side);
15034     }
15035   }
15036   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15037   {
15038     if (mode == DF_SNAP && element != EL_BD_ROCK)
15039       return MP_NO_ACTION;
15040
15041     if (CAN_FALL(element) && dy)
15042       return MP_NO_ACTION;
15043
15044     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15045         !(element == EL_SPRING && level.use_spring_bug))
15046       return MP_NO_ACTION;
15047
15048     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15049         ((move_direction & MV_VERTICAL &&
15050           ((element_info[element].move_pattern & MV_LEFT &&
15051             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15052            (element_info[element].move_pattern & MV_RIGHT &&
15053             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15054          (move_direction & MV_HORIZONTAL &&
15055           ((element_info[element].move_pattern & MV_UP &&
15056             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15057            (element_info[element].move_pattern & MV_DOWN &&
15058             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15059       return MP_NO_ACTION;
15060
15061     /* do not push elements already moving away faster than player */
15062     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15063         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15064       return MP_NO_ACTION;
15065
15066     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15067     {
15068       if (player->push_delay_value == -1 || !player_was_pushing)
15069         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15070     }
15071     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15072     {
15073       if (player->push_delay_value == -1)
15074         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15075     }
15076     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15077     {
15078       if (!player->is_pushing)
15079         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15080     }
15081
15082     player->is_pushing = TRUE;
15083     player->is_active = TRUE;
15084
15085     if (!(IN_LEV_FIELD(nextx, nexty) &&
15086           (IS_FREE(nextx, nexty) ||
15087            (IS_SB_ELEMENT(element) &&
15088             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15089            (IS_CUSTOM_ELEMENT(element) &&
15090             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15091       return MP_NO_ACTION;
15092
15093     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15094       return MP_NO_ACTION;
15095
15096     if (player->push_delay == -1)       /* new pushing; restart delay */
15097       player->push_delay = 0;
15098
15099     if (player->push_delay < player->push_delay_value &&
15100         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15101         element != EL_SPRING && element != EL_BALLOON)
15102     {
15103       /* make sure that there is no move delay before next try to push */
15104       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15105         player->move_delay = 0;
15106
15107       return MP_NO_ACTION;
15108     }
15109
15110     if (IS_CUSTOM_ELEMENT(element) &&
15111         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15112     {
15113       if (!DigFieldByCE(nextx, nexty, element))
15114         return MP_NO_ACTION;
15115     }
15116
15117     if (IS_SB_ELEMENT(element))
15118     {
15119       if (element == EL_SOKOBAN_FIELD_FULL)
15120       {
15121         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15122         local_player->sokobanfields_still_needed++;
15123       }
15124
15125       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15126       {
15127         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15128         local_player->sokobanfields_still_needed--;
15129       }
15130
15131       Feld[x][y] = EL_SOKOBAN_OBJECT;
15132
15133       if (Back[x][y] == Back[nextx][nexty])
15134         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15135       else if (Back[x][y] != 0)
15136         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15137                                     ACTION_EMPTYING);
15138       else
15139         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15140                                     ACTION_FILLING);
15141
15142       if (local_player->sokobanfields_still_needed == 0 &&
15143           game.emulation == EMU_SOKOBAN)
15144       {
15145         PlayerWins(player);
15146
15147         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15148       }
15149     }
15150     else
15151       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15152
15153     InitMovingField(x, y, move_direction);
15154     GfxAction[x][y] = ACTION_PUSHING;
15155
15156     if (mode == DF_SNAP)
15157       ContinueMoving(x, y);
15158     else
15159       MovPos[x][y] = (dx != 0 ? dx : dy);
15160
15161     Pushed[x][y] = TRUE;
15162     Pushed[nextx][nexty] = TRUE;
15163
15164     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15165       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15166     else
15167       player->push_delay_value = -1;    /* get new value later */
15168
15169     /* check for element change _after_ element has been pushed */
15170     if (game.use_change_when_pushing_bug)
15171     {
15172       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15173                                  player->index_bit, dig_side);
15174       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15175                                           player->index_bit, dig_side);
15176     }
15177   }
15178   else if (IS_SWITCHABLE(element))
15179   {
15180     if (PLAYER_SWITCHING(player, x, y))
15181     {
15182       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15183                                           player->index_bit, dig_side);
15184
15185       return MP_ACTION;
15186     }
15187
15188     player->is_switching = TRUE;
15189     player->switch_x = x;
15190     player->switch_y = y;
15191
15192     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15193
15194     if (element == EL_ROBOT_WHEEL)
15195     {
15196       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15197       ZX = x;
15198       ZY = y;
15199
15200       game.robot_wheel_active = TRUE;
15201
15202       TEST_DrawLevelField(x, y);
15203     }
15204     else if (element == EL_SP_TERMINAL)
15205     {
15206       int xx, yy;
15207
15208       SCAN_PLAYFIELD(xx, yy)
15209       {
15210         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15211           Bang(xx, yy);
15212         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15213           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15214       }
15215     }
15216     else if (IS_BELT_SWITCH(element))
15217     {
15218       ToggleBeltSwitch(x, y);
15219     }
15220     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15221              element == EL_SWITCHGATE_SWITCH_DOWN ||
15222              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15223              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15224     {
15225       ToggleSwitchgateSwitch(x, y);
15226     }
15227     else if (element == EL_LIGHT_SWITCH ||
15228              element == EL_LIGHT_SWITCH_ACTIVE)
15229     {
15230       ToggleLightSwitch(x, y);
15231     }
15232     else if (element == EL_TIMEGATE_SWITCH ||
15233              element == EL_DC_TIMEGATE_SWITCH)
15234     {
15235       ActivateTimegateSwitch(x, y);
15236     }
15237     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15238              element == EL_BALLOON_SWITCH_RIGHT ||
15239              element == EL_BALLOON_SWITCH_UP    ||
15240              element == EL_BALLOON_SWITCH_DOWN  ||
15241              element == EL_BALLOON_SWITCH_NONE  ||
15242              element == EL_BALLOON_SWITCH_ANY)
15243     {
15244       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15245                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15246                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15247                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15248                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15249                              move_direction);
15250     }
15251     else if (element == EL_LAMP)
15252     {
15253       Feld[x][y] = EL_LAMP_ACTIVE;
15254       local_player->lights_still_needed--;
15255
15256       ResetGfxAnimation(x, y);
15257       TEST_DrawLevelField(x, y);
15258     }
15259     else if (element == EL_TIME_ORB_FULL)
15260     {
15261       Feld[x][y] = EL_TIME_ORB_EMPTY;
15262
15263       if (level.time > 0 || level.use_time_orb_bug)
15264       {
15265         TimeLeft += level.time_orb_time;
15266
15267 #if 1
15268         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15269
15270         DisplayGameControlValues();
15271 #else
15272         DrawGameValue_Time(TimeLeft);
15273 #endif
15274       }
15275
15276       ResetGfxAnimation(x, y);
15277       TEST_DrawLevelField(x, y);
15278     }
15279     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15280              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15281     {
15282       int xx, yy;
15283
15284       game.ball_state = !game.ball_state;
15285
15286       SCAN_PLAYFIELD(xx, yy)
15287       {
15288         int e = Feld[xx][yy];
15289
15290         if (game.ball_state)
15291         {
15292           if (e == EL_EMC_MAGIC_BALL)
15293             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15294           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15295             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15296         }
15297         else
15298         {
15299           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15300             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15301           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15302             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15303         }
15304       }
15305     }
15306
15307     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15308                                         player->index_bit, dig_side);
15309
15310     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15311                                         player->index_bit, dig_side);
15312
15313     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15314                                         player->index_bit, dig_side);
15315
15316     return MP_ACTION;
15317   }
15318   else
15319   {
15320     if (!PLAYER_SWITCHING(player, x, y))
15321     {
15322       player->is_switching = TRUE;
15323       player->switch_x = x;
15324       player->switch_y = y;
15325
15326       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15327                                  player->index_bit, dig_side);
15328       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15329                                           player->index_bit, dig_side);
15330
15331       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15332                                  player->index_bit, dig_side);
15333       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15334                                           player->index_bit, dig_side);
15335     }
15336
15337     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15338                                player->index_bit, dig_side);
15339     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15340                                         player->index_bit, dig_side);
15341
15342     return MP_NO_ACTION;
15343   }
15344
15345   player->push_delay = -1;
15346
15347   if (is_player)                /* function can also be called by EL_PENGUIN */
15348   {
15349     if (Feld[x][y] != element)          /* really digged/collected something */
15350     {
15351       player->is_collecting = !player->is_digging;
15352       player->is_active = TRUE;
15353     }
15354   }
15355
15356   return MP_MOVING;
15357 }
15358
15359 static boolean DigFieldByCE(int x, int y, int digging_element)
15360 {
15361   int element = Feld[x][y];
15362
15363   if (!IS_FREE(x, y))
15364   {
15365     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15366                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15367                   ACTION_BREAKING);
15368
15369     /* no element can dig solid indestructible elements */
15370     if (IS_INDESTRUCTIBLE(element) &&
15371         !IS_DIGGABLE(element) &&
15372         !IS_COLLECTIBLE(element))
15373       return FALSE;
15374
15375     if (AmoebaNr[x][y] &&
15376         (element == EL_AMOEBA_FULL ||
15377          element == EL_BD_AMOEBA ||
15378          element == EL_AMOEBA_GROWING))
15379     {
15380       AmoebaCnt[AmoebaNr[x][y]]--;
15381       AmoebaCnt2[AmoebaNr[x][y]]--;
15382     }
15383
15384     if (IS_MOVING(x, y))
15385       RemoveMovingField(x, y);
15386     else
15387     {
15388       RemoveField(x, y);
15389       TEST_DrawLevelField(x, y);
15390     }
15391
15392     /* if digged element was about to explode, prevent the explosion */
15393     ExplodeField[x][y] = EX_TYPE_NONE;
15394
15395     PlayLevelSoundAction(x, y, action);
15396   }
15397
15398   Store[x][y] = EL_EMPTY;
15399
15400 #if 1
15401   /* this makes it possible to leave the removed element again */
15402   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15403     Store[x][y] = element;
15404 #else
15405   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15406   {
15407     int move_leave_element = element_info[digging_element].move_leave_element;
15408
15409     /* this makes it possible to leave the removed element again */
15410     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15411                    element : move_leave_element);
15412   }
15413 #endif
15414
15415   return TRUE;
15416 }
15417
15418 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15419 {
15420   int jx = player->jx, jy = player->jy;
15421   int x = jx + dx, y = jy + dy;
15422   int snap_direction = (dx == -1 ? MV_LEFT  :
15423                         dx == +1 ? MV_RIGHT :
15424                         dy == -1 ? MV_UP    :
15425                         dy == +1 ? MV_DOWN  : MV_NONE);
15426   boolean can_continue_snapping = (level.continuous_snapping &&
15427                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15428
15429   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15430     return FALSE;
15431
15432   if (!player->active || !IN_LEV_FIELD(x, y))
15433     return FALSE;
15434
15435   if (dx && dy)
15436     return FALSE;
15437
15438   if (!dx && !dy)
15439   {
15440     if (player->MovPos == 0)
15441       player->is_pushing = FALSE;
15442
15443     player->is_snapping = FALSE;
15444
15445     if (player->MovPos == 0)
15446     {
15447       player->is_moving = FALSE;
15448       player->is_digging = FALSE;
15449       player->is_collecting = FALSE;
15450     }
15451
15452     return FALSE;
15453   }
15454
15455 #if USE_NEW_CONTINUOUS_SNAPPING
15456   /* prevent snapping with already pressed snap key when not allowed */
15457   if (player->is_snapping && !can_continue_snapping)
15458     return FALSE;
15459 #else
15460   if (player->is_snapping)
15461     return FALSE;
15462 #endif
15463
15464   player->MovDir = snap_direction;
15465
15466   if (player->MovPos == 0)
15467   {
15468     player->is_moving = FALSE;
15469     player->is_digging = FALSE;
15470     player->is_collecting = FALSE;
15471   }
15472
15473   player->is_dropping = FALSE;
15474   player->is_dropping_pressed = FALSE;
15475   player->drop_pressed_delay = 0;
15476
15477   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15478     return FALSE;
15479
15480   player->is_snapping = TRUE;
15481   player->is_active = TRUE;
15482
15483   if (player->MovPos == 0)
15484   {
15485     player->is_moving = FALSE;
15486     player->is_digging = FALSE;
15487     player->is_collecting = FALSE;
15488   }
15489
15490   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15491     TEST_DrawLevelField(player->last_jx, player->last_jy);
15492
15493   TEST_DrawLevelField(x, y);
15494
15495   return TRUE;
15496 }
15497
15498 static boolean DropElement(struct PlayerInfo *player)
15499 {
15500   int old_element, new_element;
15501   int dropx = player->jx, dropy = player->jy;
15502   int drop_direction = player->MovDir;
15503   int drop_side = drop_direction;
15504 #if 1
15505   int drop_element = get_next_dropped_element(player);
15506 #else
15507   int drop_element = (player->inventory_size > 0 ?
15508                       player->inventory_element[player->inventory_size - 1] :
15509                       player->inventory_infinite_element != EL_UNDEFINED ?
15510                       player->inventory_infinite_element :
15511                       player->dynabombs_left > 0 ?
15512                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15513                       EL_UNDEFINED);
15514 #endif
15515
15516   player->is_dropping_pressed = TRUE;
15517
15518   /* do not drop an element on top of another element; when holding drop key
15519      pressed without moving, dropped element must move away before the next
15520      element can be dropped (this is especially important if the next element
15521      is dynamite, which can be placed on background for historical reasons) */
15522   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15523     return MP_ACTION;
15524
15525   if (IS_THROWABLE(drop_element))
15526   {
15527     dropx += GET_DX_FROM_DIR(drop_direction);
15528     dropy += GET_DY_FROM_DIR(drop_direction);
15529
15530     if (!IN_LEV_FIELD(dropx, dropy))
15531       return FALSE;
15532   }
15533
15534   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15535   new_element = drop_element;           /* default: no change when dropping */
15536
15537   /* check if player is active, not moving and ready to drop */
15538   if (!player->active || player->MovPos || player->drop_delay > 0)
15539     return FALSE;
15540
15541   /* check if player has anything that can be dropped */
15542   if (new_element == EL_UNDEFINED)
15543     return FALSE;
15544
15545   /* check if drop key was pressed long enough for EM style dynamite */
15546   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15547     return FALSE;
15548
15549   /* check if anything can be dropped at the current position */
15550   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15551     return FALSE;
15552
15553   /* collected custom elements can only be dropped on empty fields */
15554   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15555     return FALSE;
15556
15557   if (old_element != EL_EMPTY)
15558     Back[dropx][dropy] = old_element;   /* store old element on this field */
15559
15560   ResetGfxAnimation(dropx, dropy);
15561   ResetRandomAnimationValue(dropx, dropy);
15562
15563   if (player->inventory_size > 0 ||
15564       player->inventory_infinite_element != EL_UNDEFINED)
15565   {
15566     if (player->inventory_size > 0)
15567     {
15568       player->inventory_size--;
15569
15570       DrawGameDoorValues();
15571
15572       if (new_element == EL_DYNAMITE)
15573         new_element = EL_DYNAMITE_ACTIVE;
15574       else if (new_element == EL_EM_DYNAMITE)
15575         new_element = EL_EM_DYNAMITE_ACTIVE;
15576       else if (new_element == EL_SP_DISK_RED)
15577         new_element = EL_SP_DISK_RED_ACTIVE;
15578     }
15579
15580     Feld[dropx][dropy] = new_element;
15581
15582     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15583       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15584                           el2img(Feld[dropx][dropy]), 0);
15585
15586     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15587
15588     /* needed if previous element just changed to "empty" in the last frame */
15589     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15590
15591     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15592                                player->index_bit, drop_side);
15593     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15594                                         CE_PLAYER_DROPS_X,
15595                                         player->index_bit, drop_side);
15596
15597     TestIfElementTouchesCustomElement(dropx, dropy);
15598   }
15599   else          /* player is dropping a dyna bomb */
15600   {
15601     player->dynabombs_left--;
15602
15603     Feld[dropx][dropy] = new_element;
15604
15605     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15606       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15607                           el2img(Feld[dropx][dropy]), 0);
15608
15609     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15610   }
15611
15612   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15613     InitField_WithBug1(dropx, dropy, FALSE);
15614
15615   new_element = Feld[dropx][dropy];     /* element might have changed */
15616
15617   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15618       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15619   {
15620     int move_direction, nextx, nexty;
15621
15622     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15623       MovDir[dropx][dropy] = drop_direction;
15624
15625     move_direction = MovDir[dropx][dropy];
15626     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15627     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15628
15629     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15630
15631 #if USE_FIX_IMPACT_COLLISION
15632     /* do not cause impact style collision by dropping elements that can fall */
15633     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15634 #else
15635     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15636 #endif
15637   }
15638
15639   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15640   player->is_dropping = TRUE;
15641
15642   player->drop_pressed_delay = 0;
15643   player->is_dropping_pressed = FALSE;
15644
15645   player->drop_x = dropx;
15646   player->drop_y = dropy;
15647
15648   return TRUE;
15649 }
15650
15651 /* ------------------------------------------------------------------------- */
15652 /* game sound playing functions                                              */
15653 /* ------------------------------------------------------------------------- */
15654
15655 static int *loop_sound_frame = NULL;
15656 static int *loop_sound_volume = NULL;
15657
15658 void InitPlayLevelSound()
15659 {
15660   int num_sounds = getSoundListSize();
15661
15662   checked_free(loop_sound_frame);
15663   checked_free(loop_sound_volume);
15664
15665   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15666   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15667 }
15668
15669 static void PlayLevelSound(int x, int y, int nr)
15670 {
15671   int sx = SCREENX(x), sy = SCREENY(y);
15672   int volume, stereo_position;
15673   int max_distance = 8;
15674   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15675
15676   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15677       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15678     return;
15679
15680   if (!IN_LEV_FIELD(x, y) ||
15681       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15682       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15683     return;
15684
15685   volume = SOUND_MAX_VOLUME;
15686
15687   if (!IN_SCR_FIELD(sx, sy))
15688   {
15689     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15690     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15691
15692     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15693   }
15694
15695   stereo_position = (SOUND_MAX_LEFT +
15696                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15697                      (SCR_FIELDX + 2 * max_distance));
15698
15699   if (IS_LOOP_SOUND(nr))
15700   {
15701     /* This assures that quieter loop sounds do not overwrite louder ones,
15702        while restarting sound volume comparison with each new game frame. */
15703
15704     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15705       return;
15706
15707     loop_sound_volume[nr] = volume;
15708     loop_sound_frame[nr] = FrameCounter;
15709   }
15710
15711   PlaySoundExt(nr, volume, stereo_position, type);
15712 }
15713
15714 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15715 {
15716   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15717                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15718                  y < LEVELY(BY1) ? LEVELY(BY1) :
15719                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15720                  sound_action);
15721 }
15722
15723 static void PlayLevelSoundAction(int x, int y, int action)
15724 {
15725   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15726 }
15727
15728 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15729 {
15730   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15731
15732   if (sound_effect != SND_UNDEFINED)
15733     PlayLevelSound(x, y, sound_effect);
15734 }
15735
15736 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15737                                               int action)
15738 {
15739   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15740
15741   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15742     PlayLevelSound(x, y, sound_effect);
15743 }
15744
15745 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15746 {
15747   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15748
15749   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15750     PlayLevelSound(x, y, sound_effect);
15751 }
15752
15753 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15754 {
15755   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15756
15757   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15758     StopSound(sound_effect);
15759 }
15760
15761 static void PlayLevelMusic()
15762 {
15763   if (levelset.music[level_nr] != MUS_UNDEFINED)
15764     PlayMusic(levelset.music[level_nr]);        /* from config file */
15765   else
15766     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15767 }
15768
15769 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15770 {
15771   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15772   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15773   int x = xx - 1 - offset;
15774   int y = yy - 1 - offset;
15775
15776   switch (sample)
15777   {
15778     case SAMPLE_blank:
15779       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15780       break;
15781
15782     case SAMPLE_roll:
15783       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15784       break;
15785
15786     case SAMPLE_stone:
15787       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15788       break;
15789
15790     case SAMPLE_nut:
15791       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15792       break;
15793
15794     case SAMPLE_crack:
15795       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15796       break;
15797
15798     case SAMPLE_bug:
15799       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15800       break;
15801
15802     case SAMPLE_tank:
15803       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15804       break;
15805
15806     case SAMPLE_android_clone:
15807       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15808       break;
15809
15810     case SAMPLE_android_move:
15811       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15812       break;
15813
15814     case SAMPLE_spring:
15815       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15816       break;
15817
15818     case SAMPLE_slurp:
15819       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15820       break;
15821
15822     case SAMPLE_eater:
15823       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15824       break;
15825
15826     case SAMPLE_eater_eat:
15827       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15828       break;
15829
15830     case SAMPLE_alien:
15831       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15832       break;
15833
15834     case SAMPLE_collect:
15835       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15836       break;
15837
15838     case SAMPLE_diamond:
15839       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15840       break;
15841
15842     case SAMPLE_squash:
15843       /* !!! CHECK THIS !!! */
15844 #if 1
15845       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15846 #else
15847       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15848 #endif
15849       break;
15850
15851     case SAMPLE_wonderfall:
15852       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15853       break;
15854
15855     case SAMPLE_drip:
15856       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15857       break;
15858
15859     case SAMPLE_push:
15860       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15861       break;
15862
15863     case SAMPLE_dirt:
15864       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15865       break;
15866
15867     case SAMPLE_acid:
15868       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15869       break;
15870
15871     case SAMPLE_ball:
15872       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15873       break;
15874
15875     case SAMPLE_grow:
15876       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15877       break;
15878
15879     case SAMPLE_wonder:
15880       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15881       break;
15882
15883     case SAMPLE_door:
15884       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15885       break;
15886
15887     case SAMPLE_exit_open:
15888       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15889       break;
15890
15891     case SAMPLE_exit_leave:
15892       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15893       break;
15894
15895     case SAMPLE_dynamite:
15896       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15897       break;
15898
15899     case SAMPLE_tick:
15900       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15901       break;
15902
15903     case SAMPLE_press:
15904       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15905       break;
15906
15907     case SAMPLE_wheel:
15908       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15909       break;
15910
15911     case SAMPLE_boom:
15912       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15913       break;
15914
15915     case SAMPLE_die:
15916       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15917       break;
15918
15919     case SAMPLE_time:
15920       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15921       break;
15922
15923     default:
15924       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15925       break;
15926   }
15927 }
15928
15929 #if 0
15930 void ChangeTime(int value)
15931 {
15932   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15933
15934   *time += value;
15935
15936   /* EMC game engine uses value from time counter of RND game engine */
15937   level.native_em_level->lev->time = *time;
15938
15939   DrawGameValue_Time(*time);
15940 }
15941
15942 void RaiseScore(int value)
15943 {
15944   /* EMC game engine and RND game engine have separate score counters */
15945   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15946                 &level.native_em_level->lev->score : &local_player->score);
15947
15948   *score += value;
15949
15950   DrawGameValue_Score(*score);
15951 }
15952 #endif
15953
15954 void RaiseScore(int value)
15955 {
15956   local_player->score += value;
15957
15958 #if 1
15959   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15960
15961   DisplayGameControlValues();
15962 #else
15963   DrawGameValue_Score(local_player->score);
15964 #endif
15965 }
15966
15967 void RaiseScoreElement(int element)
15968 {
15969   switch (element)
15970   {
15971     case EL_EMERALD:
15972     case EL_BD_DIAMOND:
15973     case EL_EMERALD_YELLOW:
15974     case EL_EMERALD_RED:
15975     case EL_EMERALD_PURPLE:
15976     case EL_SP_INFOTRON:
15977       RaiseScore(level.score[SC_EMERALD]);
15978       break;
15979     case EL_DIAMOND:
15980       RaiseScore(level.score[SC_DIAMOND]);
15981       break;
15982     case EL_CRYSTAL:
15983       RaiseScore(level.score[SC_CRYSTAL]);
15984       break;
15985     case EL_PEARL:
15986       RaiseScore(level.score[SC_PEARL]);
15987       break;
15988     case EL_BUG:
15989     case EL_BD_BUTTERFLY:
15990     case EL_SP_ELECTRON:
15991       RaiseScore(level.score[SC_BUG]);
15992       break;
15993     case EL_SPACESHIP:
15994     case EL_BD_FIREFLY:
15995     case EL_SP_SNIKSNAK:
15996       RaiseScore(level.score[SC_SPACESHIP]);
15997       break;
15998     case EL_YAMYAM:
15999     case EL_DARK_YAMYAM:
16000       RaiseScore(level.score[SC_YAMYAM]);
16001       break;
16002     case EL_ROBOT:
16003       RaiseScore(level.score[SC_ROBOT]);
16004       break;
16005     case EL_PACMAN:
16006       RaiseScore(level.score[SC_PACMAN]);
16007       break;
16008     case EL_NUT:
16009       RaiseScore(level.score[SC_NUT]);
16010       break;
16011     case EL_DYNAMITE:
16012     case EL_EM_DYNAMITE:
16013     case EL_SP_DISK_RED:
16014     case EL_DYNABOMB_INCREASE_NUMBER:
16015     case EL_DYNABOMB_INCREASE_SIZE:
16016     case EL_DYNABOMB_INCREASE_POWER:
16017       RaiseScore(level.score[SC_DYNAMITE]);
16018       break;
16019     case EL_SHIELD_NORMAL:
16020     case EL_SHIELD_DEADLY:
16021       RaiseScore(level.score[SC_SHIELD]);
16022       break;
16023     case EL_EXTRA_TIME:
16024       RaiseScore(level.extra_time_score);
16025       break;
16026     case EL_KEY_1:
16027     case EL_KEY_2:
16028     case EL_KEY_3:
16029     case EL_KEY_4:
16030     case EL_EM_KEY_1:
16031     case EL_EM_KEY_2:
16032     case EL_EM_KEY_3:
16033     case EL_EM_KEY_4:
16034     case EL_EMC_KEY_5:
16035     case EL_EMC_KEY_6:
16036     case EL_EMC_KEY_7:
16037     case EL_EMC_KEY_8:
16038     case EL_DC_KEY_WHITE:
16039       RaiseScore(level.score[SC_KEY]);
16040       break;
16041     default:
16042       RaiseScore(element_info[element].collect_score);
16043       break;
16044   }
16045 }
16046
16047 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16048 {
16049   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16050   {
16051 #if defined(NETWORK_AVALIABLE)
16052     if (options.network)
16053       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16054     else
16055 #endif
16056     {
16057       if (quick_quit)
16058       {
16059 #if 1
16060
16061 #if 1
16062         FadeSkipNextFadeIn();
16063 #else
16064         fading = fading_none;
16065 #endif
16066
16067 #else
16068         OpenDoor(DOOR_CLOSE_1);
16069 #endif
16070
16071         game_status = GAME_MODE_MAIN;
16072
16073 #if 1
16074         DrawAndFadeInMainMenu(REDRAW_FIELD);
16075 #else
16076         DrawMainMenu();
16077 #endif
16078       }
16079       else
16080       {
16081 #if 0
16082         FadeOut(REDRAW_FIELD);
16083 #endif
16084
16085         game_status = GAME_MODE_MAIN;
16086
16087         DrawAndFadeInMainMenu(REDRAW_FIELD);
16088       }
16089     }
16090   }
16091   else          /* continue playing the game */
16092   {
16093     if (tape.playing && tape.deactivate_display)
16094       TapeDeactivateDisplayOff(TRUE);
16095
16096     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16097
16098     if (tape.playing && tape.deactivate_display)
16099       TapeDeactivateDisplayOn();
16100   }
16101 }
16102
16103 void RequestQuitGame(boolean ask_if_really_quit)
16104 {
16105   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16106   boolean skip_request = AllPlayersGone || quick_quit;
16107
16108   RequestQuitGameExt(skip_request, quick_quit,
16109                      "Do you really want to quit the game ?");
16110 }
16111
16112
16113 /* ------------------------------------------------------------------------- */
16114 /* random generator functions                                                */
16115 /* ------------------------------------------------------------------------- */
16116
16117 unsigned int InitEngineRandom_RND(long seed)
16118 {
16119   game.num_random_calls = 0;
16120
16121 #if 0
16122   unsigned int rnd_seed = InitEngineRandom(seed);
16123
16124   printf("::: START RND: %d\n", rnd_seed);
16125
16126   return rnd_seed;
16127 #else
16128
16129   return InitEngineRandom(seed);
16130
16131 #endif
16132
16133 }
16134
16135 unsigned int RND(int max)
16136 {
16137   if (max > 0)
16138   {
16139     game.num_random_calls++;
16140
16141     return GetEngineRandom(max);
16142   }
16143
16144   return 0;
16145 }
16146
16147
16148 /* ------------------------------------------------------------------------- */
16149 /* game engine snapshot handling functions                                   */
16150 /* ------------------------------------------------------------------------- */
16151
16152 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16153
16154 struct EngineSnapshotInfo
16155 {
16156   /* runtime values for custom element collect score */
16157   int collect_score[NUM_CUSTOM_ELEMENTS];
16158
16159   /* runtime values for group element choice position */
16160   int choice_pos[NUM_GROUP_ELEMENTS];
16161
16162   /* runtime values for belt position animations */
16163   int belt_graphic[4 * NUM_BELT_PARTS];
16164   int belt_anim_mode[4 * NUM_BELT_PARTS];
16165 };
16166
16167 struct EngineSnapshotNodeInfo
16168 {
16169   void *buffer_orig;
16170   void *buffer_copy;
16171   int size;
16172 };
16173
16174 static struct EngineSnapshotInfo engine_snapshot_rnd;
16175 static ListNode *engine_snapshot_list = NULL;
16176 static char *snapshot_level_identifier = NULL;
16177 static int snapshot_level_nr = -1;
16178
16179 void FreeEngineSnapshot()
16180 {
16181   while (engine_snapshot_list != NULL)
16182     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16183                        checked_free);
16184
16185   setString(&snapshot_level_identifier, NULL);
16186   snapshot_level_nr = -1;
16187 }
16188
16189 static void SaveEngineSnapshotValues_RND()
16190 {
16191   static int belt_base_active_element[4] =
16192   {
16193     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16194     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16195     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16196     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16197   };
16198   int i, j;
16199
16200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16201   {
16202     int element = EL_CUSTOM_START + i;
16203
16204     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16205   }
16206
16207   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16208   {
16209     int element = EL_GROUP_START + i;
16210
16211     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16212   }
16213
16214   for (i = 0; i < 4; i++)
16215   {
16216     for (j = 0; j < NUM_BELT_PARTS; j++)
16217     {
16218       int element = belt_base_active_element[i] + j;
16219       int graphic = el2img(element);
16220       int anim_mode = graphic_info[graphic].anim_mode;
16221
16222       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16223       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16224     }
16225   }
16226 }
16227
16228 static void LoadEngineSnapshotValues_RND()
16229 {
16230   unsigned long num_random_calls = game.num_random_calls;
16231   int i, j;
16232
16233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16234   {
16235     int element = EL_CUSTOM_START + i;
16236
16237     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16238   }
16239
16240   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16241   {
16242     int element = EL_GROUP_START + i;
16243
16244     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16245   }
16246
16247   for (i = 0; i < 4; i++)
16248   {
16249     for (j = 0; j < NUM_BELT_PARTS; j++)
16250     {
16251       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16252       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16253
16254       graphic_info[graphic].anim_mode = anim_mode;
16255     }
16256   }
16257
16258   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16259   {
16260     InitRND(tape.random_seed);
16261     for (i = 0; i < num_random_calls; i++)
16262       RND(1);
16263   }
16264
16265   if (game.num_random_calls != num_random_calls)
16266   {
16267     Error(ERR_INFO, "number of random calls out of sync");
16268     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16269     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16270     Error(ERR_EXIT, "this should not happen -- please debug");
16271   }
16272 }
16273
16274 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16275 {
16276   struct EngineSnapshotNodeInfo *bi =
16277     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16278
16279   bi->buffer_orig = buffer;
16280   bi->buffer_copy = checked_malloc(size);
16281   bi->size = size;
16282
16283   memcpy(bi->buffer_copy, buffer, size);
16284
16285   addNodeToList(&engine_snapshot_list, NULL, bi);
16286 }
16287
16288 void SaveEngineSnapshot()
16289 {
16290   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16291
16292   if (level_editor_test_game)   /* do not save snapshots from editor */
16293     return;
16294
16295   /* copy some special values to a structure better suited for the snapshot */
16296
16297   SaveEngineSnapshotValues_RND();
16298   SaveEngineSnapshotValues_EM();
16299
16300   /* save values stored in special snapshot structure */
16301
16302   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16303   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16304
16305   /* save further RND engine values */
16306
16307   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16308   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16309   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16310
16311   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16312   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16313   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16314   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16315
16316   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16317   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16318   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16319   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16320   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16321
16322   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16323   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16324   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16325
16326   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16327
16328   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16329
16330   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16331   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16332
16333   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16334   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16335   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16336   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16337   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16338   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16339   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16340   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16341   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16342   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16343   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16344   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16345   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16346   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16347   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16348   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16349   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16350   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16351
16352   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16353   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16354
16355   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16356   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16357   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16358
16359   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16360   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16361
16362   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16363   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16364   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16365   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16366   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16367
16368   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16369   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16370
16371   /* save level identification information */
16372
16373   setString(&snapshot_level_identifier, leveldir_current->identifier);
16374   snapshot_level_nr = level_nr;
16375
16376 #if 0
16377   ListNode *node = engine_snapshot_list;
16378   int num_bytes = 0;
16379
16380   while (node != NULL)
16381   {
16382     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16383
16384     node = node->next;
16385   }
16386
16387   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16388 #endif
16389 }
16390
16391 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16392 {
16393   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16394 }
16395
16396 void LoadEngineSnapshot()
16397 {
16398   ListNode *node = engine_snapshot_list;
16399
16400   if (engine_snapshot_list == NULL)
16401     return;
16402
16403   while (node != NULL)
16404   {
16405     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16406
16407     node = node->next;
16408   }
16409
16410   /* restore special values from snapshot structure */
16411
16412   LoadEngineSnapshotValues_RND();
16413   LoadEngineSnapshotValues_EM();
16414 }
16415
16416 boolean CheckEngineSnapshot()
16417 {
16418   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16419           snapshot_level_nr == level_nr);
16420 }
16421
16422
16423 /* ---------- new game button stuff ---------------------------------------- */
16424
16425 /* graphic position values for game buttons */
16426 #define GAME_BUTTON_XSIZE       30
16427 #define GAME_BUTTON_YSIZE       30
16428 #define GAME_BUTTON_XPOS        5
16429 #define GAME_BUTTON_YPOS        215
16430 #define SOUND_BUTTON_XPOS       5
16431 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16432
16433 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16434 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16435 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16436 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16437 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16438 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16439
16440 static struct
16441 {
16442   int *x, *y;
16443   int gd_x, gd_y;
16444   int gadget_id;
16445   char *infotext;
16446 } gamebutton_info[NUM_GAME_BUTTONS] =
16447 {
16448 #if 1
16449   {
16450     &game.button.stop.x,        &game.button.stop.y,
16451     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16452     GAME_CTRL_ID_STOP,
16453     "stop game"
16454   },
16455   {
16456     &game.button.pause.x,       &game.button.pause.y,
16457     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16458     GAME_CTRL_ID_PAUSE,
16459     "pause game"
16460   },
16461   {
16462     &game.button.play.x,        &game.button.play.y,
16463     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16464     GAME_CTRL_ID_PLAY,
16465     "play game"
16466   },
16467   {
16468     &game.button.sound_music.x, &game.button.sound_music.y,
16469     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16470     SOUND_CTRL_ID_MUSIC,
16471     "background music on/off"
16472   },
16473   {
16474     &game.button.sound_loops.x, &game.button.sound_loops.y,
16475     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16476     SOUND_CTRL_ID_LOOPS,
16477     "sound loops on/off"
16478   },
16479   {
16480     &game.button.sound_simple.x,&game.button.sound_simple.y,
16481     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16482     SOUND_CTRL_ID_SIMPLE,
16483     "normal sounds on/off"
16484   }
16485 #else
16486   {
16487     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16488     GAME_CTRL_ID_STOP,
16489     "stop game"
16490   },
16491   {
16492     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16493     GAME_CTRL_ID_PAUSE,
16494     "pause game"
16495   },
16496   {
16497     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16498     GAME_CTRL_ID_PLAY,
16499     "play game"
16500   },
16501   {
16502     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16503     SOUND_CTRL_ID_MUSIC,
16504     "background music on/off"
16505   },
16506   {
16507     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16508     SOUND_CTRL_ID_LOOPS,
16509     "sound loops on/off"
16510   },
16511   {
16512     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16513     SOUND_CTRL_ID_SIMPLE,
16514     "normal sounds on/off"
16515   }
16516 #endif
16517 };
16518
16519 void CreateGameButtons()
16520 {
16521   int i;
16522
16523   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16524   {
16525     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16526     struct GadgetInfo *gi;
16527     int button_type;
16528     boolean checked;
16529     unsigned long event_mask;
16530     int x, y;
16531     int gd_xoffset, gd_yoffset;
16532     int gd_x1, gd_x2, gd_y1, gd_y2;
16533     int id = i;
16534
16535     x = DX + *gamebutton_info[i].x;
16536     y = DY + *gamebutton_info[i].y;
16537     gd_xoffset = gamebutton_info[i].gd_x;
16538     gd_yoffset = gamebutton_info[i].gd_y;
16539     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16540     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16541
16542     if (id == GAME_CTRL_ID_STOP ||
16543         id == GAME_CTRL_ID_PAUSE ||
16544         id == GAME_CTRL_ID_PLAY)
16545     {
16546       button_type = GD_TYPE_NORMAL_BUTTON;
16547       checked = FALSE;
16548       event_mask = GD_EVENT_RELEASED;
16549       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16550       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16551     }
16552     else
16553     {
16554       button_type = GD_TYPE_CHECK_BUTTON;
16555       checked =
16556         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16557          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16558          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16559       event_mask = GD_EVENT_PRESSED;
16560       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16561       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16562     }
16563
16564     gi = CreateGadget(GDI_CUSTOM_ID, id,
16565                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16566 #if 1
16567                       GDI_X, x,
16568                       GDI_Y, y,
16569 #else
16570                       GDI_X, DX + gd_xoffset,
16571                       GDI_Y, DY + gd_yoffset,
16572 #endif
16573                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16574                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16575                       GDI_TYPE, button_type,
16576                       GDI_STATE, GD_BUTTON_UNPRESSED,
16577                       GDI_CHECKED, checked,
16578                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16579                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16580                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16581                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16582                       GDI_DIRECT_DRAW, FALSE,
16583                       GDI_EVENT_MASK, event_mask,
16584                       GDI_CALLBACK_ACTION, HandleGameButtons,
16585                       GDI_END);
16586
16587     if (gi == NULL)
16588       Error(ERR_EXIT, "cannot create gadget");
16589
16590     game_gadget[id] = gi;
16591   }
16592 }
16593
16594 void FreeGameButtons()
16595 {
16596   int i;
16597
16598   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16599     FreeGadget(game_gadget[i]);
16600 }
16601
16602 static void MapGameButtons()
16603 {
16604   int i;
16605
16606   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16607     MapGadget(game_gadget[i]);
16608 }
16609
16610 void UnmapGameButtons()
16611 {
16612   int i;
16613
16614   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16615     UnmapGadget(game_gadget[i]);
16616 }
16617
16618 void RedrawGameButtons()
16619 {
16620   int i;
16621
16622   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16623     RedrawGadget(game_gadget[i]);
16624 }
16625
16626 static void HandleGameButtons(struct GadgetInfo *gi)
16627 {
16628   int id = gi->custom_id;
16629
16630   if (game_status != GAME_MODE_PLAYING)
16631     return;
16632
16633   switch (id)
16634   {
16635     case GAME_CTRL_ID_STOP:
16636       if (tape.playing)
16637         TapeStop();
16638       else
16639         RequestQuitGame(TRUE);
16640       break;
16641
16642     case GAME_CTRL_ID_PAUSE:
16643       if (options.network)
16644       {
16645 #if defined(NETWORK_AVALIABLE)
16646         if (tape.pausing)
16647           SendToServer_ContinuePlaying();
16648         else
16649           SendToServer_PausePlaying();
16650 #endif
16651       }
16652       else
16653         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16654       break;
16655
16656     case GAME_CTRL_ID_PLAY:
16657       if (tape.pausing)
16658       {
16659 #if defined(NETWORK_AVALIABLE)
16660         if (options.network)
16661           SendToServer_ContinuePlaying();
16662         else
16663 #endif
16664         {
16665           tape.pausing = FALSE;
16666           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16667         }
16668       }
16669       break;
16670
16671     case SOUND_CTRL_ID_MUSIC:
16672       if (setup.sound_music)
16673       { 
16674         setup.sound_music = FALSE;
16675         FadeMusic();
16676       }
16677       else if (audio.music_available)
16678       { 
16679         setup.sound = setup.sound_music = TRUE;
16680
16681         SetAudioMode(setup.sound);
16682
16683         PlayLevelMusic();
16684       }
16685       break;
16686
16687     case SOUND_CTRL_ID_LOOPS:
16688       if (setup.sound_loops)
16689         setup.sound_loops = FALSE;
16690       else if (audio.loops_available)
16691       {
16692         setup.sound = setup.sound_loops = TRUE;
16693         SetAudioMode(setup.sound);
16694       }
16695       break;
16696
16697     case SOUND_CTRL_ID_SIMPLE:
16698       if (setup.sound_simple)
16699         setup.sound_simple = FALSE;
16700       else if (audio.sound_available)
16701       {
16702         setup.sound = setup.sound_simple = TRUE;
16703         SetAudioMode(setup.sound);
16704       }
16705       break;
16706
16707     default:
16708       break;
16709   }
16710 }