rnd-20080206-2-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_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
69
70 #if USE_DELAYED_GFX_REDRAW
71 #define TEST_DrawLevelField(x, y)                               \
72         GfxRedraw[x][y] |= GFX_REDRAW_TILE
73 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
75 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
77 #define TEST_DrawTwinkleOnField(x, y)                           \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
79 #else
80 #define TEST_DrawLevelField(x, y)                               \
81              DrawLevelField(x, y)
82 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
83              DrawLevelFieldCrumbledSand(x, y)
84 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
85              DrawLevelFieldCrumbledSandNeighbours(x, y)
86 #define TEST_DrawTwinkleOnField(x, y)                           \
87              DrawTwinkleOnField(x, y)
88 #endif
89
90
91 /* for DigField() */
92 #define DF_NO_PUSH              0
93 #define DF_DIG                  1
94 #define DF_SNAP                 2
95
96 /* for MovePlayer() */
97 #define MP_NO_ACTION            0
98 #define MP_MOVING               1
99 #define MP_ACTION               2
100 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
101
102 /* for ScrollPlayer() */
103 #define SCROLL_INIT             0
104 #define SCROLL_GO_ON            1
105
106 /* for Bang()/Explode() */
107 #define EX_PHASE_START          0
108 #define EX_TYPE_NONE            0
109 #define EX_TYPE_NORMAL          (1 << 0)
110 #define EX_TYPE_CENTER          (1 << 1)
111 #define EX_TYPE_BORDER          (1 << 2)
112 #define EX_TYPE_CROSS           (1 << 3)
113 #define EX_TYPE_DYNA            (1 << 4)
114 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
115
116 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
117 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
118 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
119 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
120
121 /* special positions in the game control window (relative to control window) */
122 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
123 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
124 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
125 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
126 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
127 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
128 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
129 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
130 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
131 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
132 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
133 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
134 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
135 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
136 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
137 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
138
139 /* special positions in the game control window (relative to main window) */
140 #define DX_LEVEL1               (DX + XX_LEVEL1)
141 #define DX_LEVEL2               (DX + XX_LEVEL2)
142 #define DX_LEVEL                (DX + XX_LEVEL)
143 #define DY_LEVEL                (DY + YY_LEVEL)
144 #define DX_EMERALDS             (DX + XX_EMERALDS)
145 #define DY_EMERALDS             (DY + YY_EMERALDS)
146 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
147 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
148 #define DX_KEYS                 (DX + XX_KEYS)
149 #define DY_KEYS                 (DY + YY_KEYS)
150 #define DX_SCORE                (DX + XX_SCORE)
151 #define DY_SCORE                (DY + YY_SCORE)
152 #define DX_TIME1                (DX + XX_TIME1)
153 #define DX_TIME2                (DX + XX_TIME2)
154 #define DX_TIME                 (DX + XX_TIME)
155 #define DY_TIME                 (DY + YY_TIME)
156
157 #if 1
158 /* game panel display and control definitions */
159
160 #define GAME_PANEL_LEVEL_NUMBER                 0
161 #define GAME_PANEL_GEMS                         1
162 #define GAME_PANEL_INVENTORY_COUNT              2
163 #define GAME_PANEL_INVENTORY_FIRST_1            3
164 #define GAME_PANEL_INVENTORY_FIRST_2            4
165 #define GAME_PANEL_INVENTORY_FIRST_3            5
166 #define GAME_PANEL_INVENTORY_FIRST_4            6
167 #define GAME_PANEL_INVENTORY_FIRST_5            7
168 #define GAME_PANEL_INVENTORY_FIRST_6            8
169 #define GAME_PANEL_INVENTORY_FIRST_7            9
170 #define GAME_PANEL_INVENTORY_FIRST_8            10
171 #define GAME_PANEL_INVENTORY_LAST_1             11
172 #define GAME_PANEL_INVENTORY_LAST_2             12
173 #define GAME_PANEL_INVENTORY_LAST_3             13
174 #define GAME_PANEL_INVENTORY_LAST_4             14
175 #define GAME_PANEL_INVENTORY_LAST_5             15
176 #define GAME_PANEL_INVENTORY_LAST_6             16
177 #define GAME_PANEL_INVENTORY_LAST_7             17
178 #define GAME_PANEL_INVENTORY_LAST_8             18
179 #define GAME_PANEL_KEY_1                        19
180 #define GAME_PANEL_KEY_2                        20
181 #define GAME_PANEL_KEY_3                        21
182 #define GAME_PANEL_KEY_4                        22
183 #define GAME_PANEL_KEY_5                        23
184 #define GAME_PANEL_KEY_6                        24
185 #define GAME_PANEL_KEY_7                        25
186 #define GAME_PANEL_KEY_8                        26
187 #define GAME_PANEL_KEY_WHITE                    27
188 #define GAME_PANEL_KEY_WHITE_COUNT              28
189 #define GAME_PANEL_SCORE                        29
190 #define GAME_PANEL_HIGHSCORE                    30
191 #define GAME_PANEL_TIME                         31
192 #define GAME_PANEL_TIME_HH                      32
193 #define GAME_PANEL_TIME_MM                      33
194 #define GAME_PANEL_TIME_SS                      34
195 #define GAME_PANEL_SHIELD_NORMAL                35
196 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
197 #define GAME_PANEL_SHIELD_DEADLY                37
198 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
199 #define GAME_PANEL_EXIT                         39
200 #define GAME_PANEL_EMC_MAGIC_BALL               40
201 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
202 #define GAME_PANEL_LIGHT_SWITCH                 42
203 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
204 #define GAME_PANEL_TIMEGATE_SWITCH              44
205 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
206 #define GAME_PANEL_SWITCHGATE_SWITCH            46
207 #define GAME_PANEL_EMC_LENSES                   47
208 #define GAME_PANEL_EMC_LENSES_TIME              48
209 #define GAME_PANEL_EMC_MAGNIFIER                49
210 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
211 #define GAME_PANEL_BALLOON_SWITCH               51
212 #define GAME_PANEL_DYNABOMB_NUMBER              52
213 #define GAME_PANEL_DYNABOMB_SIZE                53
214 #define GAME_PANEL_DYNABOMB_POWER               54
215 #define GAME_PANEL_PENGUINS                     55
216 #define GAME_PANEL_SOKOBAN_OBJECTS              56
217 #define GAME_PANEL_SOKOBAN_FIELDS               57
218 #define GAME_PANEL_ROBOT_WHEEL                  58
219 #define GAME_PANEL_CONVEYOR_BELT_1              59
220 #define GAME_PANEL_CONVEYOR_BELT_2              60
221 #define GAME_PANEL_CONVEYOR_BELT_3              61
222 #define GAME_PANEL_CONVEYOR_BELT_4              62
223 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
224 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
225 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
226 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
227 #define GAME_PANEL_MAGIC_WALL                   67
228 #define GAME_PANEL_MAGIC_WALL_TIME              68
229 #define GAME_PANEL_GRAVITY_STATE                69
230 #define GAME_PANEL_GRAPHIC_1                    70
231 #define GAME_PANEL_GRAPHIC_2                    71
232 #define GAME_PANEL_GRAPHIC_3                    72
233 #define GAME_PANEL_GRAPHIC_4                    73
234 #define GAME_PANEL_GRAPHIC_5                    74
235 #define GAME_PANEL_GRAPHIC_6                    75
236 #define GAME_PANEL_GRAPHIC_7                    76
237 #define GAME_PANEL_GRAPHIC_8                    77
238 #define GAME_PANEL_ELEMENT_1                    78
239 #define GAME_PANEL_ELEMENT_2                    79
240 #define GAME_PANEL_ELEMENT_3                    80
241 #define GAME_PANEL_ELEMENT_4                    81
242 #define GAME_PANEL_ELEMENT_5                    82
243 #define GAME_PANEL_ELEMENT_6                    83
244 #define GAME_PANEL_ELEMENT_7                    84
245 #define GAME_PANEL_ELEMENT_8                    85
246 #define GAME_PANEL_ELEMENT_COUNT_1              86
247 #define GAME_PANEL_ELEMENT_COUNT_2              87
248 #define GAME_PANEL_ELEMENT_COUNT_3              88
249 #define GAME_PANEL_ELEMENT_COUNT_4              89
250 #define GAME_PANEL_ELEMENT_COUNT_5              90
251 #define GAME_PANEL_ELEMENT_COUNT_6              91
252 #define GAME_PANEL_ELEMENT_COUNT_7              92
253 #define GAME_PANEL_ELEMENT_COUNT_8              93
254 #define GAME_PANEL_CE_SCORE_1                   94
255 #define GAME_PANEL_CE_SCORE_2                   95
256 #define GAME_PANEL_CE_SCORE_3                   96
257 #define GAME_PANEL_CE_SCORE_4                   97
258 #define GAME_PANEL_CE_SCORE_5                   98
259 #define GAME_PANEL_CE_SCORE_6                   99
260 #define GAME_PANEL_CE_SCORE_7                   100
261 #define GAME_PANEL_CE_SCORE_8                   101
262 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
263 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
264 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
265 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
266 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
267 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
268 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
269 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
270 #define GAME_PANEL_PLAYER_NAME                  110
271 #define GAME_PANEL_LEVEL_NAME                   111
272 #define GAME_PANEL_LEVEL_AUTHOR                 112
273
274 #define NUM_GAME_PANEL_CONTROLS                 113
275
276 struct GamePanelOrderInfo
277 {
278   int nr;
279   int sort_priority;
280 };
281
282 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
283
284 struct GamePanelControlInfo
285 {
286   int nr;
287
288   struct TextPosInfo *pos;
289   int type;
290
291   int value, last_value;
292   int frame, last_frame;
293   int gfx_frame;
294   int gfx_random;
295 };
296
297 static struct GamePanelControlInfo game_panel_controls[] =
298 {
299   {
300     GAME_PANEL_LEVEL_NUMBER,
301     &game.panel.level_number,
302     TYPE_INTEGER,
303   },
304   {
305     GAME_PANEL_GEMS,
306     &game.panel.gems,
307     TYPE_INTEGER,
308   },
309   {
310     GAME_PANEL_INVENTORY_COUNT,
311     &game.panel.inventory_count,
312     TYPE_INTEGER,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_1,
316     &game.panel.inventory_first[0],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_FIRST_2,
321     &game.panel.inventory_first[1],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_FIRST_3,
326     &game.panel.inventory_first[2],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_FIRST_4,
331     &game.panel.inventory_first[3],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_FIRST_5,
336     &game.panel.inventory_first[4],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_FIRST_6,
341     &game.panel.inventory_first[5],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_FIRST_7,
346     &game.panel.inventory_first[6],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_FIRST_8,
351     &game.panel.inventory_first[7],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_1,
356     &game.panel.inventory_last[0],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_INVENTORY_LAST_2,
361     &game.panel.inventory_last[1],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_INVENTORY_LAST_3,
366     &game.panel.inventory_last[2],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_INVENTORY_LAST_4,
371     &game.panel.inventory_last[3],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_INVENTORY_LAST_5,
376     &game.panel.inventory_last[4],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_INVENTORY_LAST_6,
381     &game.panel.inventory_last[5],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_INVENTORY_LAST_7,
386     &game.panel.inventory_last[6],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_INVENTORY_LAST_8,
391     &game.panel.inventory_last[7],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_1,
396     &game.panel.key[0],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_2,
401     &game.panel.key[1],
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_3,
406     &game.panel.key[2],
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_PANEL_KEY_4,
411     &game.panel.key[3],
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_KEY_5,
416     &game.panel.key[4],
417     TYPE_ELEMENT,
418   },
419   {
420     GAME_PANEL_KEY_6,
421     &game.panel.key[5],
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_KEY_7,
426     &game.panel.key[6],
427     TYPE_ELEMENT,
428   },
429   {
430     GAME_PANEL_KEY_8,
431     &game.panel.key[7],
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_KEY_WHITE,
436     &game.panel.key_white,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_KEY_WHITE_COUNT,
441     &game.panel.key_white_count,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_SCORE,
446     &game.panel.score,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_PANEL_HIGHSCORE,
451     &game.panel.highscore,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIME,
456     &game.panel.time,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_PANEL_TIME_HH,
461     &game.panel.time_hh,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_TIME_MM,
466     &game.panel.time_mm,
467     TYPE_INTEGER,
468   },
469   {
470     GAME_PANEL_TIME_SS,
471     &game.panel.time_ss,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_SHIELD_NORMAL,
476     &game.panel.shield_normal,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_SHIELD_NORMAL_TIME,
481     &game.panel.shield_normal_time,
482     TYPE_INTEGER,
483   },
484   {
485     GAME_PANEL_SHIELD_DEADLY,
486     &game.panel.shield_deadly,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_SHIELD_DEADLY_TIME,
491     &game.panel.shield_deadly_time,
492     TYPE_INTEGER,
493   },
494   {
495     GAME_PANEL_EXIT,
496     &game.panel.exit,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_EMC_MAGIC_BALL,
501     &game.panel.emc_magic_ball,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
506     &game.panel.emc_magic_ball_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_LIGHT_SWITCH,
511     &game.panel.light_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_LIGHT_SWITCH_TIME,
516     &game.panel.light_switch_time,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_TIMEGATE_SWITCH,
521     &game.panel.timegate_switch,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_PANEL_TIMEGATE_SWITCH_TIME,
526     &game.panel.timegate_switch_time,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_SWITCHGATE_SWITCH,
531     &game.panel.switchgate_switch,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_EMC_LENSES,
536     &game.panel.emc_lenses,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_EMC_LENSES_TIME,
541     &game.panel.emc_lenses_time,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_EMC_MAGNIFIER,
546     &game.panel.emc_magnifier,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_EMC_MAGNIFIER_TIME,
551     &game.panel.emc_magnifier_time,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_BALLOON_SWITCH,
556     &game.panel.balloon_switch,
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_DYNABOMB_NUMBER,
561     &game.panel.dynabomb_number,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_DYNABOMB_SIZE,
566     &game.panel.dynabomb_size,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_DYNABOMB_POWER,
571     &game.panel.dynabomb_power,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_PENGUINS,
576     &game.panel.penguins,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_SOKOBAN_OBJECTS,
581     &game.panel.sokoban_objects,
582     TYPE_INTEGER,
583   },
584   {
585     GAME_PANEL_SOKOBAN_FIELDS,
586     &game.panel.sokoban_fields,
587     TYPE_INTEGER,
588   },
589   {
590     GAME_PANEL_ROBOT_WHEEL,
591     &game.panel.robot_wheel,
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1,
596     &game.panel.conveyor_belt[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2,
601     &game.panel.conveyor_belt[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3,
606     &game.panel.conveyor_belt[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4,
611     &game.panel.conveyor_belt[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
616     &game.panel.conveyor_belt_switch[0],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
621     &game.panel.conveyor_belt_switch[1],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
626     &game.panel.conveyor_belt_switch[2],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
631     &game.panel.conveyor_belt_switch[3],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_MAGIC_WALL,
636     &game.panel.magic_wall,
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_MAGIC_WALL_TIME,
641     &game.panel.magic_wall_time,
642     TYPE_INTEGER,
643   },
644   {
645     GAME_PANEL_GRAVITY_STATE,
646     &game.panel.gravity_state,
647     TYPE_STRING,
648   },
649   {
650     GAME_PANEL_GRAPHIC_1,
651     &game.panel.graphic[0],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_2,
656     &game.panel.graphic[1],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_3,
661     &game.panel.graphic[2],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_4,
666     &game.panel.graphic[3],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_GRAPHIC_5,
671     &game.panel.graphic[4],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_GRAPHIC_6,
676     &game.panel.graphic[5],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_GRAPHIC_7,
681     &game.panel.graphic[6],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_GRAPHIC_8,
686     &game.panel.graphic[7],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_1,
691     &game.panel.element[0],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_2,
696     &game.panel.element[1],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_3,
701     &game.panel.element[2],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_4,
706     &game.panel.element[3],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_5,
711     &game.panel.element[4],
712     TYPE_ELEMENT,
713   },
714   {
715     GAME_PANEL_ELEMENT_6,
716     &game.panel.element[5],
717     TYPE_ELEMENT,
718   },
719   {
720     GAME_PANEL_ELEMENT_7,
721     &game.panel.element[6],
722     TYPE_ELEMENT,
723   },
724   {
725     GAME_PANEL_ELEMENT_8,
726     &game.panel.element[7],
727     TYPE_ELEMENT,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_1,
731     &game.panel.element_count[0],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_2,
736     &game.panel.element_count[1],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_3,
741     &game.panel.element_count[2],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_4,
746     &game.panel.element_count[3],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_ELEMENT_COUNT_5,
751     &game.panel.element_count[4],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_ELEMENT_COUNT_6,
756     &game.panel.element_count[5],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_ELEMENT_COUNT_7,
761     &game.panel.element_count[6],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_ELEMENT_COUNT_8,
766     &game.panel.element_count[7],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_1,
771     &game.panel.ce_score[0],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_2,
776     &game.panel.ce_score[1],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_3,
781     &game.panel.ce_score[2],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_4,
786     &game.panel.ce_score[3],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_5,
791     &game.panel.ce_score[4],
792     TYPE_INTEGER,
793   },
794   {
795     GAME_PANEL_CE_SCORE_6,
796     &game.panel.ce_score[5],
797     TYPE_INTEGER,
798   },
799   {
800     GAME_PANEL_CE_SCORE_7,
801     &game.panel.ce_score[6],
802     TYPE_INTEGER,
803   },
804   {
805     GAME_PANEL_CE_SCORE_8,
806     &game.panel.ce_score[7],
807     TYPE_INTEGER,
808   },
809   {
810     GAME_PANEL_CE_SCORE_1_ELEMENT,
811     &game.panel.ce_score_element[0],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_2_ELEMENT,
816     &game.panel.ce_score_element[1],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_3_ELEMENT,
821     &game.panel.ce_score_element[2],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_4_ELEMENT,
826     &game.panel.ce_score_element[3],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_CE_SCORE_5_ELEMENT,
831     &game.panel.ce_score_element[4],
832     TYPE_ELEMENT,
833   },
834   {
835     GAME_PANEL_CE_SCORE_6_ELEMENT,
836     &game.panel.ce_score_element[5],
837     TYPE_ELEMENT,
838   },
839   {
840     GAME_PANEL_CE_SCORE_7_ELEMENT,
841     &game.panel.ce_score_element[6],
842     TYPE_ELEMENT,
843   },
844   {
845     GAME_PANEL_CE_SCORE_8_ELEMENT,
846     &game.panel.ce_score_element[7],
847     TYPE_ELEMENT,
848   },
849   {
850     GAME_PANEL_PLAYER_NAME,
851     &game.panel.player_name,
852     TYPE_STRING,
853   },
854   {
855     GAME_PANEL_LEVEL_NAME,
856     &game.panel.level_name,
857     TYPE_STRING,
858   },
859   {
860     GAME_PANEL_LEVEL_AUTHOR,
861     &game.panel.level_author,
862     TYPE_STRING,
863   },
864
865   {
866     -1,
867     NULL,
868     -1,
869   }
870 };
871 #endif
872
873
874 /* values for delayed check of falling and moving elements and for collision */
875 #define CHECK_DELAY_MOVING      3
876 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
877 #define CHECK_DELAY_COLLISION   2
878 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
879
880 /* values for initial player move delay (initial delay counter value) */
881 #define INITIAL_MOVE_DELAY_OFF  -1
882 #define INITIAL_MOVE_DELAY_ON   0
883
884 /* values for player movement speed (which is in fact a delay value) */
885 #define MOVE_DELAY_MIN_SPEED    32
886 #define MOVE_DELAY_NORMAL_SPEED 8
887 #define MOVE_DELAY_HIGH_SPEED   4
888 #define MOVE_DELAY_MAX_SPEED    1
889
890 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
891 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
892
893 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
894 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
895
896 /* values for other actions */
897 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
898 #define MOVE_STEPSIZE_MIN       (1)
899 #define MOVE_STEPSIZE_MAX       (TILEX)
900
901 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
902 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
903
904 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
905
906 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
907                                  RND(element_info[e].push_delay_random))
908 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
909                                  RND(element_info[e].drop_delay_random))
910 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
911                                  RND(element_info[e].move_delay_random))
912 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                     (element_info[e].move_delay_random))
914 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
915                                  RND(element_info[e].ce_value_random_initial))
916 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
917 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
918                                  RND((c)->delay_random * (c)->delay_frames))
919 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
920                                  RND((c)->delay_random))
921
922
923 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
924          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
925
926 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
927         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
928          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
929          (be) + (e) - EL_SELF)
930
931 #define GET_PLAYER_FROM_BITS(p)                                         \
932         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
933
934 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
935         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
936          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
937          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
938          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
939          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
940          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
941          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
942          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
943          (e))
944
945 #define CAN_GROW_INTO(e)                                                \
946         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
950                                         (condition)))
951
952 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
953                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
954                                         (CAN_MOVE_INTO_ACID(e) &&       \
955                                          Feld[x][y] == EL_ACID) ||      \
956                                         (condition)))
957
958 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
959                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
960                                         (CAN_MOVE_INTO_ACID(e) &&       \
961                                          Feld[x][y] == EL_ACID) ||      \
962                                         (condition)))
963
964 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
965                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
966                                         (condition) ||                  \
967                                         (CAN_MOVE_INTO_ACID(e) &&       \
968                                          Feld[x][y] == EL_ACID) ||      \
969                                         (DONT_COLLIDE_WITH(e) &&        \
970                                          IS_PLAYER(x, y) &&             \
971                                          !PLAYER_ENEMY_PROTECTED(x, y))))
972
973 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
975
976 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
978
979 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
981
982 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
983         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
984                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
985
986 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
987         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988
989 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
990         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
991
992 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
993         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
994
995 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
996         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
997
998 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
999         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1000
1001 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1002         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1003                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1004                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1006                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1007 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1008         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1009
1010 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1011         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1012
1013 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1014         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1015
1016 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1017         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1018                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1019
1020 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1021
1022 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1023                 (!IS_PLAYER(x, y) &&                                    \
1024                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1025
1026 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1027         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1028
1029 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1030 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1031
1032 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1033 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1034 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1035 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1036
1037 /* game button identifiers */
1038 #define GAME_CTRL_ID_STOP               0
1039 #define GAME_CTRL_ID_PAUSE              1
1040 #define GAME_CTRL_ID_PLAY               2
1041 #define SOUND_CTRL_ID_MUSIC             3
1042 #define SOUND_CTRL_ID_LOOPS             4
1043 #define SOUND_CTRL_ID_SIMPLE            5
1044
1045 #define NUM_GAME_BUTTONS                6
1046
1047
1048 /* forward declaration for internal use */
1049
1050 static void CreateField(int, int, int);
1051
1052 static void ResetGfxAnimation(int, int);
1053
1054 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1055 static void AdvanceFrameAndPlayerCounters(int);
1056
1057 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1058 static boolean MovePlayer(struct PlayerInfo *, int, int);
1059 static void ScrollPlayer(struct PlayerInfo *, int);
1060 static void ScrollScreen(struct PlayerInfo *, int);
1061
1062 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1063 static boolean DigFieldByCE(int, int, int);
1064 static boolean SnapField(struct PlayerInfo *, int, int);
1065 static boolean DropElement(struct PlayerInfo *);
1066
1067 static void InitBeltMovement(void);
1068 static void CloseAllOpenTimegates(void);
1069 static void CheckGravityMovement(struct PlayerInfo *);
1070 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1071 static void KillPlayerUnlessEnemyProtected(int, int);
1072 static void KillPlayerUnlessExplosionProtected(int, int);
1073
1074 static void TestIfPlayerTouchesCustomElement(int, int);
1075 static void TestIfElementTouchesCustomElement(int, int);
1076 static void TestIfElementHitsCustomElement(int, int, int);
1077 #if 0
1078 static void TestIfElementSmashesCustomElement(int, int, int);
1079 #endif
1080
1081 static void HandleElementChange(int, int, int);
1082 static void ExecuteCustomElementAction(int, int, int, int);
1083 static boolean ChangeElement(int, int, int, int);
1084
1085 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1086 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1087         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1088 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1089         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1090 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1092 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1093         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1094
1095 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1096 #define CheckElementChange(x, y, e, te, ev)                             \
1097         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1098 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1099         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1100 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1101         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1102
1103 static void PlayLevelSound(int, int, int);
1104 static void PlayLevelSoundNearest(int, int, int);
1105 static void PlayLevelSoundAction(int, int, int);
1106 static void PlayLevelSoundElementAction(int, int, int, int);
1107 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1108 static void PlayLevelSoundActionIfLoop(int, int, int);
1109 static void StopLevelSoundActionIfLoop(int, int, int);
1110 static void PlayLevelMusic();
1111
1112 static void MapGameButtons();
1113 static void HandleGameButtons(struct GadgetInfo *);
1114
1115 int AmoebeNachbarNr(int, int);
1116 void AmoebeUmwandeln(int, int);
1117 void ContinueMoving(int, int);
1118 void Bang(int, int);
1119 void InitMovDir(int, int);
1120 void InitAmoebaNr(int, int);
1121 int NewHiScore(void);
1122
1123 void TestIfGoodThingHitsBadThing(int, int, int);
1124 void TestIfBadThingHitsGoodThing(int, int, int);
1125 void TestIfPlayerTouchesBadThing(int, int);
1126 void TestIfPlayerRunsIntoBadThing(int, int, int);
1127 void TestIfBadThingTouchesPlayer(int, int);
1128 void TestIfBadThingRunsIntoPlayer(int, int, int);
1129 void TestIfFriendTouchesBadThing(int, int);
1130 void TestIfBadThingTouchesFriend(int, int);
1131 void TestIfBadThingTouchesOtherBadThing(int, int);
1132 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1133
1134 void KillPlayer(struct PlayerInfo *);
1135 void BuryPlayer(struct PlayerInfo *);
1136 void RemovePlayer(struct PlayerInfo *);
1137
1138 static int getInvisibleActiveFromInvisibleElement(int);
1139 static int getInvisibleFromInvisibleActiveElement(int);
1140
1141 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1142
1143 /* for detection of endless loops, caused by custom element programming */
1144 /* (using maximal playfield width x 10 is just a rough approximation) */
1145 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1146
1147 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1148 {                                                                       \
1149   if (recursion_loop_detected)                                          \
1150     return (rc);                                                        \
1151                                                                         \
1152   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1153   {                                                                     \
1154     recursion_loop_detected = TRUE;                                     \
1155     recursion_loop_element = (e);                                       \
1156   }                                                                     \
1157                                                                         \
1158   recursion_loop_depth++;                                               \
1159 }
1160
1161 #define RECURSION_LOOP_DETECTION_END()                                  \
1162 {                                                                       \
1163   recursion_loop_depth--;                                               \
1164 }
1165
1166 static int recursion_loop_depth;
1167 static boolean recursion_loop_detected;
1168 static boolean recursion_loop_element;
1169
1170
1171 /* ------------------------------------------------------------------------- */
1172 /* definition of elements that automatically change to other elements after  */
1173 /* a specified time, eventually calling a function when changing             */
1174 /* ------------------------------------------------------------------------- */
1175
1176 /* forward declaration for changer functions */
1177 static void InitBuggyBase(int, int);
1178 static void WarnBuggyBase(int, int);
1179
1180 static void InitTrap(int, int);
1181 static void ActivateTrap(int, int);
1182 static void ChangeActiveTrap(int, int);
1183
1184 static void InitRobotWheel(int, int);
1185 static void RunRobotWheel(int, int);
1186 static void StopRobotWheel(int, int);
1187
1188 static void InitTimegateWheel(int, int);
1189 static void RunTimegateWheel(int, int);
1190
1191 static void InitMagicBallDelay(int, int);
1192 static void ActivateMagicBall(int, int);
1193
1194 struct ChangingElementInfo
1195 {
1196   int element;
1197   int target_element;
1198   int change_delay;
1199   void (*pre_change_function)(int x, int y);
1200   void (*change_function)(int x, int y);
1201   void (*post_change_function)(int x, int y);
1202 };
1203
1204 static struct ChangingElementInfo change_delay_list[] =
1205 {
1206   {
1207     EL_NUT_BREAKING,
1208     EL_EMERALD,
1209     6,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_PEARL_BREAKING,
1216     EL_EMPTY,
1217     8,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_EXIT_OPENING,
1224     EL_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_EXIT_CLOSING,
1232     EL_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_STEEL_EXIT_OPENING,
1240     EL_STEEL_EXIT_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_STEEL_EXIT_CLOSING,
1248     EL_STEEL_EXIT_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_EM_EXIT_OPENING,
1256     EL_EM_EXIT_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_EM_EXIT_CLOSING,
1264 #if 1
1265     EL_EMPTY,
1266 #else
1267     EL_EM_EXIT_CLOSED,
1268 #endif
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_OPENING,
1276     EL_EM_STEEL_EXIT_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_EM_STEEL_EXIT_CLOSING,
1284 #if 1
1285     EL_STEELWALL,
1286 #else
1287     EL_EM_STEEL_EXIT_CLOSED,
1288 #endif
1289     29,
1290     NULL,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_EXIT_OPENING,
1296     EL_SP_EXIT_OPEN,
1297     29,
1298     NULL,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_EXIT_CLOSING,
1304     EL_SP_EXIT_CLOSED,
1305     29,
1306     NULL,
1307     NULL,
1308     NULL
1309   },
1310   {
1311     EL_SWITCHGATE_OPENING,
1312     EL_SWITCHGATE_OPEN,
1313     29,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318   {
1319     EL_SWITCHGATE_CLOSING,
1320     EL_SWITCHGATE_CLOSED,
1321     29,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_TIMEGATE_OPENING,
1328     EL_TIMEGATE_OPEN,
1329     29,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_TIMEGATE_CLOSING,
1336     EL_TIMEGATE_CLOSED,
1337     29,
1338     NULL,
1339     NULL,
1340     NULL
1341   },
1342
1343   {
1344     EL_ACID_SPLASH_LEFT,
1345     EL_EMPTY,
1346     8,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351   {
1352     EL_ACID_SPLASH_RIGHT,
1353     EL_EMPTY,
1354     8,
1355     NULL,
1356     NULL,
1357     NULL
1358   },
1359   {
1360     EL_SP_BUGGY_BASE,
1361     EL_SP_BUGGY_BASE_ACTIVATING,
1362     0,
1363     InitBuggyBase,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_SP_BUGGY_BASE_ACTIVATING,
1369     EL_SP_BUGGY_BASE_ACTIVE,
1370     0,
1371     InitBuggyBase,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_SP_BUGGY_BASE_ACTIVE,
1377     EL_SP_BUGGY_BASE,
1378     0,
1379     InitBuggyBase,
1380     WarnBuggyBase,
1381     NULL
1382   },
1383   {
1384     EL_TRAP,
1385     EL_TRAP_ACTIVE,
1386     0,
1387     InitTrap,
1388     NULL,
1389     ActivateTrap
1390   },
1391   {
1392     EL_TRAP_ACTIVE,
1393     EL_TRAP,
1394     31,
1395     NULL,
1396     ChangeActiveTrap,
1397     NULL
1398   },
1399   {
1400     EL_ROBOT_WHEEL_ACTIVE,
1401     EL_ROBOT_WHEEL,
1402     0,
1403     InitRobotWheel,
1404     RunRobotWheel,
1405     StopRobotWheel
1406   },
1407   {
1408     EL_TIMEGATE_SWITCH_ACTIVE,
1409     EL_TIMEGATE_SWITCH,
1410     0,
1411     InitTimegateWheel,
1412     RunTimegateWheel,
1413     NULL
1414   },
1415   {
1416     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1417     EL_DC_TIMEGATE_SWITCH,
1418     0,
1419     InitTimegateWheel,
1420     RunTimegateWheel,
1421     NULL
1422   },
1423   {
1424     EL_EMC_MAGIC_BALL_ACTIVE,
1425     EL_EMC_MAGIC_BALL_ACTIVE,
1426     0,
1427     InitMagicBallDelay,
1428     NULL,
1429     ActivateMagicBall
1430   },
1431   {
1432     EL_EMC_SPRING_BUMPER_ACTIVE,
1433     EL_EMC_SPRING_BUMPER,
1434     8,
1435     NULL,
1436     NULL,
1437     NULL
1438   },
1439   {
1440     EL_DIAGONAL_SHRINKING,
1441     EL_UNDEFINED,
1442     0,
1443     NULL,
1444     NULL,
1445     NULL
1446   },
1447   {
1448     EL_DIAGONAL_GROWING,
1449     EL_UNDEFINED,
1450     0,
1451     NULL,
1452     NULL,
1453     NULL,
1454   },
1455
1456   {
1457     EL_UNDEFINED,
1458     EL_UNDEFINED,
1459     -1,
1460     NULL,
1461     NULL,
1462     NULL
1463   }
1464 };
1465
1466 struct
1467 {
1468   int element;
1469   int push_delay_fixed, push_delay_random;
1470 }
1471 push_delay_list[] =
1472 {
1473   { EL_SPRING,                  0, 0 },
1474   { EL_BALLOON,                 0, 0 },
1475
1476   { EL_SOKOBAN_OBJECT,          2, 0 },
1477   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1478   { EL_SATELLITE,               2, 0 },
1479   { EL_SP_DISK_YELLOW,          2, 0 },
1480
1481   { EL_UNDEFINED,               0, 0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int move_stepsize;
1488 }
1489 move_stepsize_list[] =
1490 {
1491   { EL_AMOEBA_DROP,             2 },
1492   { EL_AMOEBA_DROPPING,         2 },
1493   { EL_QUICKSAND_FILLING,       1 },
1494   { EL_QUICKSAND_EMPTYING,      1 },
1495   { EL_QUICKSAND_FAST_FILLING,  2 },
1496   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1497   { EL_MAGIC_WALL_FILLING,      2 },
1498   { EL_MAGIC_WALL_EMPTYING,     2 },
1499   { EL_BD_MAGIC_WALL_FILLING,   2 },
1500   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1501   { EL_DC_MAGIC_WALL_FILLING,   2 },
1502   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1503
1504   { EL_UNDEFINED,               0 },
1505 };
1506
1507 struct
1508 {
1509   int element;
1510   int count;
1511 }
1512 collect_count_list[] =
1513 {
1514   { EL_EMERALD,                 1 },
1515   { EL_BD_DIAMOND,              1 },
1516   { EL_EMERALD_YELLOW,          1 },
1517   { EL_EMERALD_RED,             1 },
1518   { EL_EMERALD_PURPLE,          1 },
1519   { EL_DIAMOND,                 3 },
1520   { EL_SP_INFOTRON,             1 },
1521   { EL_PEARL,                   5 },
1522   { EL_CRYSTAL,                 8 },
1523
1524   { EL_UNDEFINED,               0 },
1525 };
1526
1527 struct
1528 {
1529   int element;
1530   int direction;
1531 }
1532 access_direction_list[] =
1533 {
1534   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1535   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1536   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1537   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1538   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1540   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1541   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1542   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1543   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1544   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1545
1546   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1547   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1548   { EL_SP_PORT_UP,                                                   MV_DOWN },
1549   { EL_SP_PORT_DOWN,                                         MV_UP           },
1550   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1551   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1552   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1553   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1554   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1555   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1556   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1557   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1558   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1559   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1560   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1561   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1562   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1563   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1564   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1565
1566   { EL_UNDEFINED,                       MV_NONE                              }
1567 };
1568
1569 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1570
1571 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1572 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1573 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1574                                  IS_JUST_CHANGING(x, y))
1575
1576 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1577
1578 /* static variables for playfield scan mode (scanning forward or backward) */
1579 static int playfield_scan_start_x = 0;
1580 static int playfield_scan_start_y = 0;
1581 static int playfield_scan_delta_x = 1;
1582 static int playfield_scan_delta_y = 1;
1583
1584 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1585                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1586                                      (y) += playfield_scan_delta_y)     \
1587                                 for ((x) = playfield_scan_start_x;      \
1588                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1589                                      (x) += playfield_scan_delta_x)
1590
1591 #ifdef DEBUG
1592 void DEBUG_SetMaximumDynamite()
1593 {
1594   int i;
1595
1596   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1597     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1598       local_player->inventory_element[local_player->inventory_size++] =
1599         EL_DYNAMITE;
1600 }
1601 #endif
1602
1603 static void InitPlayfieldScanModeVars()
1604 {
1605   if (game.use_reverse_scan_direction)
1606   {
1607     playfield_scan_start_x = lev_fieldx - 1;
1608     playfield_scan_start_y = lev_fieldy - 1;
1609
1610     playfield_scan_delta_x = -1;
1611     playfield_scan_delta_y = -1;
1612   }
1613   else
1614   {
1615     playfield_scan_start_x = 0;
1616     playfield_scan_start_y = 0;
1617
1618     playfield_scan_delta_x = 1;
1619     playfield_scan_delta_y = 1;
1620   }
1621 }
1622
1623 static void InitPlayfieldScanMode(int mode)
1624 {
1625   game.use_reverse_scan_direction =
1626     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1627
1628   InitPlayfieldScanModeVars();
1629 }
1630
1631 static int get_move_delay_from_stepsize(int move_stepsize)
1632 {
1633   move_stepsize =
1634     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1635
1636   /* make sure that stepsize value is always a power of 2 */
1637   move_stepsize = (1 << log_2(move_stepsize));
1638
1639   return TILEX / move_stepsize;
1640 }
1641
1642 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1643                                boolean init_game)
1644 {
1645   int player_nr = player->index_nr;
1646   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1647   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1648
1649   /* do no immediately change move delay -- the player might just be moving */
1650   player->move_delay_value_next = move_delay;
1651
1652   /* information if player can move must be set separately */
1653   player->cannot_move = cannot_move;
1654
1655   if (init_game)
1656   {
1657     player->move_delay       = game.initial_move_delay[player_nr];
1658     player->move_delay_value = game.initial_move_delay_value[player_nr];
1659
1660     player->move_delay_value_next = -1;
1661
1662     player->move_delay_reset_counter = 0;
1663   }
1664 }
1665
1666 void GetPlayerConfig()
1667 {
1668   GameFrameDelay = setup.game_frame_delay;
1669
1670   if (!audio.sound_available)
1671     setup.sound_simple = FALSE;
1672
1673   if (!audio.loops_available)
1674     setup.sound_loops = FALSE;
1675
1676   if (!audio.music_available)
1677     setup.sound_music = FALSE;
1678
1679   if (!video.fullscreen_available)
1680     setup.fullscreen = FALSE;
1681
1682   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1683
1684   SetAudioMode(setup.sound);
1685   InitJoysticks();
1686 }
1687
1688 int GetElementFromGroupElement(int element)
1689 {
1690   if (IS_GROUP_ELEMENT(element))
1691   {
1692     struct ElementGroupInfo *group = element_info[element].group;
1693     int last_anim_random_frame = gfx.anim_random_frame;
1694     int element_pos;
1695
1696     if (group->choice_mode == ANIM_RANDOM)
1697       gfx.anim_random_frame = RND(group->num_elements_resolved);
1698
1699     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1700                                     group->choice_mode, 0,
1701                                     group->choice_pos);
1702
1703     if (group->choice_mode == ANIM_RANDOM)
1704       gfx.anim_random_frame = last_anim_random_frame;
1705
1706     group->choice_pos++;
1707
1708     element = group->element_resolved[element_pos];
1709   }
1710
1711   return element;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Feld[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Feld[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     /* ---------- initialize player's last field block delay --------------- */
1751
1752     /* always start with reliable default value (no adjustment needed) */
1753     player->block_delay_adjustment = 0;
1754
1755     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     /* special case 2: in game engines before 3.1.1, blocking was different */
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!options.network || player->connected)
1764     {
1765       player->active = TRUE;
1766
1767       /* remove potentially duplicate players */
1768       if (StorePlayer[jx][jy] == Feld[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Feld[x][y];
1772
1773       if (options.debug)
1774       {
1775         printf("Player %d activated.\n", player->element_nr);
1776         printf("[Local player is %d and currently %s.]\n",
1777                local_player->element_nr,
1778                local_player->active ? "active" : "not active");
1779       }
1780     }
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788 #if USE_PLAYER_REANIMATION
1789   if (!init_game)
1790   {
1791     int player_nr = GET_PLAYER_NR(element);
1792     struct PlayerInfo *player = &stored_player[player_nr];
1793
1794     if (player->active && player->killed)
1795       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1796   }
1797 #endif
1798 }
1799
1800 static void InitField(int x, int y, boolean init_game)
1801 {
1802   int element = Feld[x][y];
1803
1804   switch (element)
1805   {
1806     case EL_SP_MURPHY:
1807     case EL_PLAYER_1:
1808     case EL_PLAYER_2:
1809     case EL_PLAYER_3:
1810     case EL_PLAYER_4:
1811       InitPlayerField(x, y, element, init_game);
1812       break;
1813
1814     case EL_SOKOBAN_FIELD_PLAYER:
1815       element = Feld[x][y] = EL_PLAYER_1;
1816       InitField(x, y, init_game);
1817
1818       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1819       InitField(x, y, init_game);
1820       break;
1821
1822     case EL_SOKOBAN_FIELD_EMPTY:
1823       local_player->sokobanfields_still_needed++;
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       local_player->lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       local_player->friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    /* more than one switch -- set it like the first switch */
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950 #if !USE_BOTH_SWITCHGATE_SWITCHES
1951     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1952       if (init_game)
1953         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1954       break;
1955
1956     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1957       if (init_game)
1958         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1959       break;
1960 #endif
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_state)
1977         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_state)
1982         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       /* reference elements should not be used on the playfield */
2010       Feld[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019 #if USE_NEW_CUSTOM_VALUE
2020         if (!element_info[element].use_last_ce_value || init_game)
2021           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2022 #endif
2023       }
2024       else if (IS_GROUP_ELEMENT(element))
2025       {
2026         Feld[x][y] = GetElementFromGroupElement(element);
2027
2028         InitField(x, y, init_game);
2029       }
2030
2031       break;
2032   }
2033
2034   if (!init_game)
2035     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2036 }
2037
2038 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2039 {
2040   InitField(x, y, init_game);
2041
2042   /* not needed to call InitMovDir() -- already done by InitField()! */
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(Feld[x][y]))
2045     InitMovDir(x, y);
2046 }
2047
2048 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2049 {
2050   int old_element = Feld[x][y];
2051
2052   InitField(x, y, init_game);
2053
2054   /* not needed to call InitMovDir() -- already done by InitField()! */
2055   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2056       CAN_MOVE(old_element) &&
2057       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2058     InitMovDir(x, y);
2059
2060   /* this case is in fact a combination of not less than three bugs:
2061      first, it calls InitMovDir() for elements that can move, although this is
2062      already done by InitField(); then, it checks the element that was at this
2063      field _before_ the call to InitField() (which can change it); lastly, it
2064      was not called for "mole with direction" elements, which were treated as
2065      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2066   */
2067 }
2068
2069 #if 1
2070
2071 static int get_key_element_from_nr(int key_nr)
2072 {
2073   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2074                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2075                           EL_EM_KEY_1 : EL_KEY_1);
2076
2077   return key_base_element + key_nr;
2078 }
2079
2080 static int get_next_dropped_element(struct PlayerInfo *player)
2081 {
2082   return (player->inventory_size > 0 ?
2083           player->inventory_element[player->inventory_size - 1] :
2084           player->inventory_infinite_element != EL_UNDEFINED ?
2085           player->inventory_infinite_element :
2086           player->dynabombs_left > 0 ?
2087           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2088           EL_UNDEFINED);
2089 }
2090
2091 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2092 {
2093   /* pos >= 0: get element from bottom of the stack;
2094      pos <  0: get element from top of the stack */
2095
2096   if (pos < 0)
2097   {
2098     int min_inventory_size = -pos;
2099     int inventory_pos = player->inventory_size - min_inventory_size;
2100     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2101
2102     return (player->inventory_size >= min_inventory_size ?
2103             player->inventory_element[inventory_pos] :
2104             player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             EL_UNDEFINED);
2109   }
2110   else
2111   {
2112     int min_dynabombs_left = pos + 1;
2113     int min_inventory_size = pos + 1 - player->dynabombs_left;
2114     int inventory_pos = pos - player->dynabombs_left;
2115
2116     return (player->inventory_infinite_element != EL_UNDEFINED ?
2117             player->inventory_infinite_element :
2118             player->dynabombs_left >= min_dynabombs_left ?
2119             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2120             player->inventory_size >= min_inventory_size ?
2121             player->inventory_element[inventory_pos] :
2122             EL_UNDEFINED);
2123   }
2124 }
2125
2126 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2127 {
2128   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2129   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2130   int compare_result;
2131
2132   if (gpo1->sort_priority != gpo2->sort_priority)
2133     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2134   else
2135     compare_result = gpo1->nr - gpo2->nr;
2136
2137   return compare_result;
2138 }
2139
2140 void InitGameControlValues()
2141 {
2142   int i;
2143
2144   for (i = 0; game_panel_controls[i].nr != -1; i++)
2145   {
2146     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2147     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2148     struct TextPosInfo *pos = gpc->pos;
2149     int nr = gpc->nr;
2150     int type = gpc->type;
2151
2152     if (nr != i)
2153     {
2154       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2155       Error(ERR_EXIT, "this should not happen -- please debug");
2156     }
2157
2158     /* force update of game controls after initialization */
2159     gpc->value = gpc->last_value = -1;
2160     gpc->frame = gpc->last_frame = -1;
2161     gpc->gfx_frame = -1;
2162
2163     /* determine panel value width for later calculation of alignment */
2164     if (type == TYPE_INTEGER || type == TYPE_STRING)
2165     {
2166       pos->width = pos->size * getFontWidth(pos->font);
2167       pos->height = getFontHeight(pos->font);
2168     }
2169     else if (type == TYPE_ELEMENT)
2170     {
2171       pos->width = pos->size;
2172       pos->height = pos->size;
2173     }
2174
2175     /* fill structure for game panel draw order */
2176     gpo->nr = gpc->nr;
2177     gpo->sort_priority = pos->sort_priority;
2178   }
2179
2180   /* sort game panel controls according to sort_priority and control number */
2181   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2182         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2183 }
2184
2185 void UpdatePlayfieldElementCount()
2186 {
2187   boolean use_element_count = FALSE;
2188   int i, j, x, y;
2189
2190   /* first check if it is needed at all to calculate playfield element count */
2191   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2192     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2193       use_element_count = TRUE;
2194
2195   if (!use_element_count)
2196     return;
2197
2198   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2199     element_info[i].element_count = 0;
2200
2201   SCAN_PLAYFIELD(x, y)
2202   {
2203     element_info[Feld[x][y]].element_count++;
2204   }
2205
2206   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2207     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2208       if (IS_IN_GROUP(j, i))
2209         element_info[EL_GROUP_START + i].element_count +=
2210           element_info[j].element_count;
2211 }
2212
2213 void UpdateGameControlValues()
2214 {
2215   int i, k;
2216   int time = (local_player->LevelSolved ?
2217               local_player->LevelSolved_CountingTime :
2218               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2219               level.native_em_level->lev->time :
2220               level.time == 0 ? TimePlayed : TimeLeft);
2221   int score = (local_player->LevelSolved ?
2222                local_player->LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                level.native_em_level->lev->score :
2225                local_player->score);
2226   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227               level.native_em_level->lev->required :
2228               local_player->gems_still_needed);
2229   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230                      level.native_em_level->lev->required > 0 :
2231                      local_player->gems_still_needed > 0 ||
2232                      local_player->sokobanfields_still_needed > 0 ||
2233                      local_player->lights_still_needed > 0);
2234
2235   UpdatePlayfieldElementCount();
2236
2237   /* update game panel control values */
2238
2239   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2240   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2241
2242   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2243   for (i = 0; i < MAX_NUM_KEYS; i++)
2244     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2245   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2246   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2247
2248   if (game.centered_player_nr == -1)
2249   {
2250     for (i = 0; i < MAX_PLAYERS; i++)
2251     {
2252       for (k = 0; k < MAX_NUM_KEYS; k++)
2253       {
2254         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2255         {
2256           if (level.native_em_level->ply[i]->keys & (1 << k))
2257             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2258               get_key_element_from_nr(k);
2259         }
2260         else if (stored_player[i].key[k])
2261           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262             get_key_element_from_nr(k);
2263       }
2264
2265       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2266         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2267           level.native_em_level->ply[i]->dynamite;
2268       else
2269         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2270           stored_player[i].inventory_size;
2271
2272       if (stored_player[i].num_white_keys > 0)
2273         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2274           EL_DC_KEY_WHITE;
2275
2276       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2277         stored_player[i].num_white_keys;
2278     }
2279   }
2280   else
2281   {
2282     int player_nr = game.centered_player_nr;
2283
2284     for (k = 0; k < MAX_NUM_KEYS; k++)
2285     {
2286       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287       {
2288         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292       else if (stored_player[player_nr].key[k])
2293         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294           get_key_element_from_nr(k);
2295     }
2296
2297     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299         level.native_em_level->ply[player_nr]->dynamite;
2300     else
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         stored_player[player_nr].inventory_size;
2303
2304     if (stored_player[player_nr].num_white_keys > 0)
2305       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2306
2307     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2308       stored_player[player_nr].num_white_keys;
2309   }
2310
2311   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2312   {
2313     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2314       get_inventory_element_from_pos(local_player, i);
2315     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2316       get_inventory_element_from_pos(local_player, -i - 1);
2317   }
2318
2319   game_panel_controls[GAME_PANEL_SCORE].value = score;
2320   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2321
2322   game_panel_controls[GAME_PANEL_TIME].value = time;
2323
2324   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2325   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2326   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2327
2328   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2329     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2332     local_player->shield_normal_time_left;
2333   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2334     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2335      EL_EMPTY);
2336   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2337     local_player->shield_deadly_time_left;
2338
2339   game_panel_controls[GAME_PANEL_EXIT].value =
2340     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2341
2342   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2343     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2344   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2345     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2346      EL_EMC_MAGIC_BALL_SWITCH);
2347
2348   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2349     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2350   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2351     game.light_time_left;
2352
2353   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2354     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2355   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2356     game.timegate_time_left;
2357
2358   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2359     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2360
2361   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2362     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2364     game.lenses_time_left;
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2367     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2368   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2369     game.magnify_time_left;
2370
2371   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2372     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2373      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2374      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2375      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2376      EL_BALLOON_SWITCH_NONE);
2377
2378   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2379     local_player->dynabomb_count;
2380   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2381     local_player->dynabomb_size;
2382   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2383     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2384
2385   game_panel_controls[GAME_PANEL_PENGUINS].value =
2386     local_player->friends_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2389     local_player->sokobanfields_still_needed;
2390   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2391     local_player->sokobanfields_still_needed;
2392
2393   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2394     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2395
2396   for (i = 0; i < NUM_BELTS; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2399       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2400        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2401     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2402       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2403   }
2404
2405   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2406     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2407   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2408     game.magic_wall_time_left;
2409
2410 #if USE_PLAYER_GRAVITY
2411   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2412     local_player->gravity;
2413 #else
2414   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2415 #endif
2416
2417   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2418     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2419
2420   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2421     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2422       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2423        game.panel.element[i].id : EL_UNDEFINED);
2424
2425   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2426     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2427       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2428        element_info[game.panel.element_count[i].id].element_count : 0);
2429
2430   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2431     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2432       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2433        element_info[game.panel.ce_score[i].id].collect_score : 0);
2434
2435   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2436     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2437       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2438        element_info[game.panel.ce_score_element[i].id].collect_score :
2439        EL_UNDEFINED);
2440
2441   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2442   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2443   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2444
2445   /* update game panel control frames */
2446
2447   for (i = 0; game_panel_controls[i].nr != -1; i++)
2448   {
2449     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2450
2451     if (gpc->type == TYPE_ELEMENT)
2452     {
2453       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2454       {
2455         int last_anim_random_frame = gfx.anim_random_frame;
2456         int element = gpc->value;
2457         int graphic = el2panelimg(element);
2458
2459         if (gpc->value != gpc->last_value)
2460         {
2461           gpc->gfx_frame = 0;
2462           gpc->gfx_random = INIT_GFX_RANDOM();
2463         }
2464         else
2465         {
2466           gpc->gfx_frame++;
2467
2468           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2469               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2470             gpc->gfx_random = INIT_GFX_RANDOM();
2471         }
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = gpc->gfx_random;
2475
2476         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2477           gpc->gfx_frame = element_info[element].collect_score;
2478
2479         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2480                                               gpc->gfx_frame);
2481
2482         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2483           gfx.anim_random_frame = last_anim_random_frame;
2484       }
2485     }
2486   }
2487 }
2488
2489 void DisplayGameControlValues()
2490 {
2491   boolean redraw_panel = FALSE;
2492   int i;
2493
2494   for (i = 0; game_panel_controls[i].nr != -1; i++)
2495   {
2496     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2497
2498     if (PANEL_DEACTIVATED(gpc->pos))
2499       continue;
2500
2501     if (gpc->value == gpc->last_value &&
2502         gpc->frame == gpc->last_frame)
2503       continue;
2504
2505     redraw_panel = TRUE;
2506   }
2507
2508   if (!redraw_panel)
2509     return;
2510
2511   /* copy default game door content to main double buffer */
2512   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2513              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2514
2515   /* redraw game control buttons */
2516 #if 1
2517   RedrawGameButtons();
2518 #else
2519   UnmapGameButtons();
2520   MapGameButtons();
2521 #endif
2522
2523   game_status = GAME_MODE_PSEUDO_PANEL;
2524
2525 #if 1
2526   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2527 #else
2528   for (i = 0; game_panel_controls[i].nr != -1; i++)
2529 #endif
2530   {
2531 #if 1
2532     int nr = game_panel_order[i].nr;
2533     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2534 #else
2535     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2536     int nr = gpc->nr;
2537 #endif
2538     struct TextPosInfo *pos = gpc->pos;
2539     int type = gpc->type;
2540     int value = gpc->value;
2541     int frame = gpc->frame;
2542 #if 0
2543     int last_value = gpc->last_value;
2544     int last_frame = gpc->last_frame;
2545 #endif
2546     int size = pos->size;
2547     int font = pos->font;
2548     boolean draw_masked = pos->draw_masked;
2549     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2550
2551     if (PANEL_DEACTIVATED(pos))
2552       continue;
2553
2554 #if 0
2555     if (value == last_value && frame == last_frame)
2556       continue;
2557 #endif
2558
2559     gpc->last_value = value;
2560     gpc->last_frame = frame;
2561
2562 #if 0
2563     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2564 #endif
2565
2566     if (type == TYPE_INTEGER)
2567     {
2568       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2569           nr == GAME_PANEL_TIME)
2570       {
2571         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2572
2573         if (use_dynamic_size)           /* use dynamic number of digits */
2574         {
2575           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2576           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2577           int size2 = size1 + 1;
2578           int font1 = pos->font;
2579           int font2 = pos->font_alt;
2580
2581           size = (value < value_change ? size1 : size2);
2582           font = (value < value_change ? font1 : font2);
2583
2584 #if 0
2585           /* clear background if value just changed its size (dynamic digits) */
2586           if ((last_value < value_change) != (value < value_change))
2587           {
2588             int width1 = size1 * getFontWidth(font1);
2589             int width2 = size2 * getFontWidth(font2);
2590             int max_width = MAX(width1, width2);
2591             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2592
2593             pos->width = max_width;
2594
2595             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2596                                        max_width, max_height);
2597           }
2598 #endif
2599         }
2600       }
2601
2602 #if 1
2603       /* correct text size if "digits" is zero or less */
2604       if (size <= 0)
2605         size = strlen(int2str(value, size));
2606
2607       /* dynamically correct text alignment */
2608       pos->width = size * getFontWidth(font);
2609 #endif
2610
2611       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2612                   int2str(value, size), font, mask_mode);
2613     }
2614     else if (type == TYPE_ELEMENT)
2615     {
2616       int element, graphic;
2617       Bitmap *src_bitmap;
2618       int src_x, src_y;
2619       int width, height;
2620       int dst_x = PANEL_XPOS(pos);
2621       int dst_y = PANEL_YPOS(pos);
2622
2623 #if 1
2624       if (value != EL_UNDEFINED && value != EL_EMPTY)
2625       {
2626         element = value;
2627         graphic = el2panelimg(value);
2628
2629         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2630
2631 #if 1
2632         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2633           size = TILESIZE;
2634 #endif
2635
2636         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2637                               &src_x, &src_y);
2638
2639         width  = graphic_info[graphic].width  * size / TILESIZE;
2640         height = graphic_info[graphic].height * size / TILESIZE;
2641
2642         if (draw_masked)
2643         {
2644           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2645                         dst_x - src_x, dst_y - src_y);
2646           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2647                            dst_x, dst_y);
2648         }
2649         else
2650         {
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653         }
2654       }
2655 #else
2656       if (value == EL_UNDEFINED || value == EL_EMPTY)
2657       {
2658         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2659         graphic = el2panelimg(element);
2660
2661         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2662         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2663         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2664       }
2665       else
2666       {
2667         element = value;
2668         graphic = el2panelimg(value);
2669
2670         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2671       }
2672
2673       width  = graphic_info[graphic].width  * size / TILESIZE;
2674       height = graphic_info[graphic].height * size / TILESIZE;
2675
2676       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2677 #endif
2678     }
2679     else if (type == TYPE_STRING)
2680     {
2681       boolean active = (value != 0);
2682       char *state_normal = "off";
2683       char *state_active = "on";
2684       char *state = (active ? state_active : state_normal);
2685       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2686                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2687                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2688                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2689
2690       if (nr == GAME_PANEL_GRAVITY_STATE)
2691       {
2692         int font1 = pos->font;          /* (used for normal state) */
2693         int font2 = pos->font_alt;      /* (used for active state) */
2694 #if 0
2695         int size1 = strlen(state_normal);
2696         int size2 = strlen(state_active);
2697         int width1 = size1 * getFontWidth(font1);
2698         int width2 = size2 * getFontWidth(font2);
2699         int max_width = MAX(width1, width2);
2700         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2701
2702         pos->width = max_width;
2703
2704         /* clear background for values that may have changed its size */
2705         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706                                    max_width, max_height);
2707 #endif
2708
2709         font = (active ? font2 : font1);
2710       }
2711
2712       if (s != NULL)
2713       {
2714         char *s_cut;
2715
2716 #if 1
2717         if (size <= 0)
2718         {
2719           /* don't truncate output if "chars" is zero or less */
2720           size = strlen(s);
2721
2722           /* dynamically correct text alignment */
2723           pos->width = size * getFontWidth(font);
2724         }
2725 #endif
2726
2727         s_cut = getStringCopyN(s, size);
2728
2729         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2730                     s_cut, font, mask_mode);
2731
2732         free(s_cut);
2733       }
2734     }
2735
2736     redraw_mask |= REDRAW_DOOR_1;
2737   }
2738
2739   game_status = GAME_MODE_PLAYING;
2740 }
2741
2742 void UpdateAndDisplayGameControlValues()
2743 {
2744   if (tape.warp_forward)
2745     return;
2746
2747   UpdateGameControlValues();
2748   DisplayGameControlValues();
2749 }
2750
2751 void DrawGameValue_Emeralds(int value)
2752 {
2753   struct TextPosInfo *pos = &game.panel.gems;
2754 #if 1
2755   int font_nr = pos->font;
2756 #else
2757   int font_nr = FONT_TEXT_2;
2758 #endif
2759   int font_width = getFontWidth(font_nr);
2760   int chars = pos->size;
2761
2762 #if 1
2763   return;       /* !!! USE NEW STUFF !!! */
2764 #endif
2765
2766   if (PANEL_DEACTIVATED(pos))
2767     return;
2768
2769   pos->width = chars * font_width;
2770
2771   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2772 }
2773
2774 void DrawGameValue_Dynamite(int value)
2775 {
2776   struct TextPosInfo *pos = &game.panel.inventory_count;
2777 #if 1
2778   int font_nr = pos->font;
2779 #else
2780   int font_nr = FONT_TEXT_2;
2781 #endif
2782   int font_width = getFontWidth(font_nr);
2783   int chars = pos->size;
2784
2785 #if 1
2786   return;       /* !!! USE NEW STUFF !!! */
2787 #endif
2788
2789   if (PANEL_DEACTIVATED(pos))
2790     return;
2791
2792   pos->width = chars * font_width;
2793
2794   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2795 }
2796
2797 void DrawGameValue_Score(int value)
2798 {
2799   struct TextPosInfo *pos = &game.panel.score;
2800 #if 1
2801   int font_nr = pos->font;
2802 #else
2803   int font_nr = FONT_TEXT_2;
2804 #endif
2805   int font_width = getFontWidth(font_nr);
2806   int chars = pos->size;
2807
2808 #if 1
2809   return;       /* !!! USE NEW STUFF !!! */
2810 #endif
2811
2812   if (PANEL_DEACTIVATED(pos))
2813     return;
2814
2815   pos->width = chars * font_width;
2816
2817   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2818 }
2819
2820 void DrawGameValue_Time(int value)
2821 {
2822   struct TextPosInfo *pos = &game.panel.time;
2823   static int last_value = -1;
2824   int chars1 = 3;
2825   int chars2 = 4;
2826   int chars = pos->size;
2827 #if 1
2828   int font1_nr = pos->font;
2829   int font2_nr = pos->font_alt;
2830 #else
2831   int font1_nr = FONT_TEXT_2;
2832   int font2_nr = FONT_TEXT_1;
2833 #endif
2834   int font_nr = font1_nr;
2835   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2836
2837 #if 1
2838   return;       /* !!! USE NEW STUFF !!! */
2839 #endif
2840
2841   if (PANEL_DEACTIVATED(pos))
2842     return;
2843
2844   if (use_dynamic_chars)                /* use dynamic number of chars */
2845   {
2846     chars   = (value < 1000 ? chars1   : chars2);
2847     font_nr = (value < 1000 ? font1_nr : font2_nr);
2848   }
2849
2850   /* clear background if value just changed its size (dynamic chars only) */
2851   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2852   {
2853     int width1 = chars1 * getFontWidth(font1_nr);
2854     int width2 = chars2 * getFontWidth(font2_nr);
2855     int max_width = MAX(width1, width2);
2856     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2857
2858     pos->width = max_width;
2859
2860     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2861                                max_width, max_height);
2862   }
2863
2864   pos->width = chars * getFontWidth(font_nr);
2865
2866   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2867
2868   last_value = value;
2869 }
2870
2871 void DrawGameValue_Level(int value)
2872 {
2873   struct TextPosInfo *pos = &game.panel.level_number;
2874   int chars1 = 2;
2875   int chars2 = 3;
2876   int chars = pos->size;
2877 #if 1
2878   int font1_nr = pos->font;
2879   int font2_nr = pos->font_alt;
2880 #else
2881   int font1_nr = FONT_TEXT_2;
2882   int font2_nr = FONT_TEXT_1;
2883 #endif
2884   int font_nr = font1_nr;
2885   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2886
2887 #if 1
2888   return;       /* !!! USE NEW STUFF !!! */
2889 #endif
2890
2891   if (PANEL_DEACTIVATED(pos))
2892     return;
2893
2894   if (use_dynamic_chars)                /* use dynamic number of chars */
2895   {
2896     chars   = (level_nr < 100 ? chars1   : chars2);
2897     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2898   }
2899
2900   pos->width = chars * getFontWidth(font_nr);
2901
2902   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2903 }
2904
2905 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2906 {
2907 #if 0
2908   struct TextPosInfo *pos = &game.panel.keys;
2909 #endif
2910 #if 0
2911   int base_key_graphic = EL_KEY_1;
2912 #endif
2913   int i;
2914
2915 #if 1
2916   return;       /* !!! USE NEW STUFF !!! */
2917 #endif
2918
2919 #if 0
2920   if (PANEL_DEACTIVATED(pos))
2921     return;
2922 #endif
2923
2924 #if 0
2925   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2926     base_key_graphic = EL_EM_KEY_1;
2927 #endif
2928
2929 #if 0
2930   pos->width = 4 * MINI_TILEX;
2931 #endif
2932
2933 #if 1
2934   for (i = 0; i < MAX_NUM_KEYS; i++)
2935 #else
2936   /* currently only 4 of 8 possible keys are displayed */
2937   for (i = 0; i < STD_NUM_KEYS; i++)
2938 #endif
2939   {
2940 #if 1
2941     struct TextPosInfo *pos = &game.panel.key[i];
2942 #endif
2943     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2944     int src_y = DOOR_GFX_PAGEY1 + 123;
2945 #if 1
2946     int dst_x = PANEL_XPOS(pos);
2947     int dst_y = PANEL_YPOS(pos);
2948 #else
2949     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2950     int dst_y = PANEL_YPOS(pos);
2951 #endif
2952
2953 #if 1
2954     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2955                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2956                    EL_KEY_1) + i;
2957     int graphic = el2edimg(element);
2958 #endif
2959
2960 #if 1
2961     if (PANEL_DEACTIVATED(pos))
2962       continue;
2963 #endif
2964
2965 #if 0
2966     /* masked blit with tiles from half-size scaled bitmap does not work yet
2967        (no mask bitmap created for these sizes after loading and scaling) --
2968        solution: load without creating mask, scale, then create final mask */
2969
2970     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2971                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2972
2973     if (key[i])
2974     {
2975 #if 0
2976       int graphic = el2edimg(base_key_graphic + i);
2977 #endif
2978       Bitmap *src_bitmap;
2979       int src_x, src_y;
2980
2981       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2982
2983       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2984                     dst_x - src_x, dst_y - src_y);
2985       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2986                        dst_x, dst_y);
2987     }
2988 #else
2989 #if 1
2990     if (key[i])
2991       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2992     else
2993       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2994                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2995 #else
2996     if (key[i])
2997       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2998     else
2999       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3000                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3001 #endif
3002 #endif
3003   }
3004 }
3005
3006 #else
3007
3008 void DrawGameValue_Emeralds(int value)
3009 {
3010   int font_nr = FONT_TEXT_2;
3011   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3012
3013   if (PANEL_DEACTIVATED(game.panel.gems))
3014     return;
3015
3016   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3017 }
3018
3019 void DrawGameValue_Dynamite(int value)
3020 {
3021   int font_nr = FONT_TEXT_2;
3022   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3023
3024   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3025     return;
3026
3027   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3028 }
3029
3030 void DrawGameValue_Score(int value)
3031 {
3032   int font_nr = FONT_TEXT_2;
3033   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3034
3035   if (PANEL_DEACTIVATED(game.panel.score))
3036     return;
3037
3038   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3039 }
3040
3041 void DrawGameValue_Time(int value)
3042 {
3043   int font1_nr = FONT_TEXT_2;
3044 #if 1
3045   int font2_nr = FONT_TEXT_1;
3046 #else
3047   int font2_nr = FONT_LEVEL_NUMBER;
3048 #endif
3049   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3050   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3051
3052   if (PANEL_DEACTIVATED(game.panel.time))
3053     return;
3054
3055   /* clear background if value just changed its size */
3056   if (value == 999 || value == 1000)
3057     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3058
3059   if (value < 1000)
3060     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3061   else
3062     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3063 }
3064
3065 void DrawGameValue_Level(int value)
3066 {
3067   int font1_nr = FONT_TEXT_2;
3068 #if 1
3069   int font2_nr = FONT_TEXT_1;
3070 #else
3071   int font2_nr = FONT_LEVEL_NUMBER;
3072 #endif
3073
3074   if (PANEL_DEACTIVATED(game.panel.level))
3075     return;
3076
3077   if (level_nr < 100)
3078     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3079   else
3080     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3081 }
3082
3083 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3084 {
3085   int base_key_graphic = EL_KEY_1;
3086   int i;
3087
3088   if (PANEL_DEACTIVATED(game.panel.keys))
3089     return;
3090
3091   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3092     base_key_graphic = EL_EM_KEY_1;
3093
3094   /* currently only 4 of 8 possible keys are displayed */
3095   for (i = 0; i < STD_NUM_KEYS; i++)
3096   {
3097     int x = XX_KEYS + i * MINI_TILEX;
3098     int y = YY_KEYS;
3099
3100     if (key[i])
3101       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3102     else
3103       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3104                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3105   }
3106 }
3107
3108 #endif
3109
3110 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3111                        int key_bits)
3112 {
3113   int key[MAX_NUM_KEYS];
3114   int i;
3115
3116   /* prevent EM engine from updating time/score values parallel to GameWon() */
3117   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3118       local_player->LevelSolved)
3119     return;
3120
3121   for (i = 0; i < MAX_NUM_KEYS; i++)
3122     key[i] = key_bits & (1 << i);
3123
3124   DrawGameValue_Level(level_nr);
3125
3126   DrawGameValue_Emeralds(emeralds);
3127   DrawGameValue_Dynamite(dynamite);
3128   DrawGameValue_Score(score);
3129   DrawGameValue_Time(time);
3130
3131   DrawGameValue_Keys(key);
3132 }
3133
3134 void UpdateGameDoorValues()
3135 {
3136   UpdateGameControlValues();
3137 }
3138
3139 void DrawGameDoorValues()
3140 {
3141   DisplayGameControlValues();
3142 }
3143
3144 void DrawGameDoorValues_OLD()
3145 {
3146   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3147   int dynamite_value = 0;
3148   int score_value = (local_player->LevelSolved ? local_player->score_final :
3149                      local_player->score);
3150   int gems_value = local_player->gems_still_needed;
3151   int key_bits = 0;
3152   int i, j;
3153
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3155   {
3156     DrawGameDoorValues_EM();
3157
3158     return;
3159   }
3160
3161   if (game.centered_player_nr == -1)
3162   {
3163     for (i = 0; i < MAX_PLAYERS; i++)
3164     {
3165       for (j = 0; j < MAX_NUM_KEYS; j++)
3166         if (stored_player[i].key[j])
3167           key_bits |= (1 << j);
3168
3169       dynamite_value += stored_player[i].inventory_size;
3170     }
3171   }
3172   else
3173   {
3174     int player_nr = game.centered_player_nr;
3175
3176     for (i = 0; i < MAX_NUM_KEYS; i++)
3177       if (stored_player[player_nr].key[i])
3178         key_bits |= (1 << i);
3179
3180     dynamite_value = stored_player[player_nr].inventory_size;
3181   }
3182
3183   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3184                     key_bits);
3185 }
3186
3187
3188 /*
3189   =============================================================================
3190   InitGameEngine()
3191   -----------------------------------------------------------------------------
3192   initialize game engine due to level / tape version number
3193   =============================================================================
3194 */
3195
3196 static void InitGameEngine()
3197 {
3198   int i, j, k, l, x, y;
3199
3200   /* set game engine from tape file when re-playing, else from level file */
3201   game.engine_version = (tape.playing ? tape.engine_version :
3202                          level.game_version);
3203
3204   /* ---------------------------------------------------------------------- */
3205   /* set flags for bugs and changes according to active game engine version */
3206   /* ---------------------------------------------------------------------- */
3207
3208   /*
3209     Summary of bugfix/change:
3210     Fixed handling for custom elements that change when pushed by the player.
3211
3212     Fixed/changed in version:
3213     3.1.0
3214
3215     Description:
3216     Before 3.1.0, custom elements that "change when pushing" changed directly
3217     after the player started pushing them (until then handled in "DigField()").
3218     Since 3.1.0, these custom elements are not changed until the "pushing"
3219     move of the element is finished (now handled in "ContinueMoving()").
3220
3221     Affected levels/tapes:
3222     The first condition is generally needed for all levels/tapes before version
3223     3.1.0, which might use the old behaviour before it was changed; known tapes
3224     that are affected are some tapes from the level set "Walpurgis Gardens" by
3225     Jamie Cullen.
3226     The second condition is an exception from the above case and is needed for
3227     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3228     above (including some development versions of 3.1.0), but before it was
3229     known that this change would break tapes like the above and was fixed in
3230     3.1.1, so that the changed behaviour was active although the engine version
3231     while recording maybe was before 3.1.0. There is at least one tape that is
3232     affected by this exception, which is the tape for the one-level set "Bug
3233     Machine" by Juergen Bonhagen.
3234   */
3235
3236   game.use_change_when_pushing_bug =
3237     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3238      !(tape.playing &&
3239        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3240        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3241
3242   /*
3243     Summary of bugfix/change:
3244     Fixed handling for blocking the field the player leaves when moving.
3245
3246     Fixed/changed in version:
3247     3.1.1
3248
3249     Description:
3250     Before 3.1.1, when "block last field when moving" was enabled, the field
3251     the player is leaving when moving was blocked for the time of the move,
3252     and was directly unblocked afterwards. This resulted in the last field
3253     being blocked for exactly one less than the number of frames of one player
3254     move. Additionally, even when blocking was disabled, the last field was
3255     blocked for exactly one frame.
3256     Since 3.1.1, due to changes in player movement handling, the last field
3257     is not blocked at all when blocking is disabled. When blocking is enabled,
3258     the last field is blocked for exactly the number of frames of one player
3259     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3260     last field is blocked for exactly one more than the number of frames of
3261     one player move.
3262
3263     Affected levels/tapes:
3264     (!!! yet to be determined -- probably many !!!)
3265   */
3266
3267   game.use_block_last_field_bug =
3268     (game.engine_version < VERSION_IDENT(3,1,1,0));
3269
3270   /*
3271     Summary of bugfix/change:
3272     Changed behaviour of CE changes with multiple changes per single frame.
3273
3274     Fixed/changed in version:
3275     3.2.0-6
3276
3277     Description:
3278     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3279     This resulted in race conditions where CEs seem to behave strange in some
3280     situations (where triggered CE changes were just skipped because there was
3281     already a CE change on that tile in the playfield in that engine frame).
3282     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3283     (The number of changes per frame must be limited in any case, because else
3284     it is easily possible to define CE changes that would result in an infinite
3285     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3286     should be set large enough so that it would only be reached in cases where
3287     the corresponding CE change conditions run into a loop. Therefore, it seems
3288     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3289     maximal number of change pages for custom elements.)
3290
3291     Affected levels/tapes:
3292     Probably many.
3293   */
3294
3295 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3296   game.max_num_changes_per_frame = 1;
3297 #else
3298   game.max_num_changes_per_frame =
3299     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3300 #endif
3301
3302   /* ---------------------------------------------------------------------- */
3303
3304   /* default scan direction: scan playfield from top/left to bottom/right */
3305   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3306
3307   /* dynamically adjust element properties according to game engine version */
3308   InitElementPropertiesEngine(game.engine_version);
3309
3310 #if 0
3311   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3312   printf("          tape version == %06d [%s] [file: %06d]\n",
3313          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3314          tape.file_version);
3315   printf("       => game.engine_version == %06d\n", game.engine_version);
3316 #endif
3317
3318   /* ---------- initialize player's initial move delay --------------------- */
3319
3320   /* dynamically adjust player properties according to level information */
3321   for (i = 0; i < MAX_PLAYERS; i++)
3322     game.initial_move_delay_value[i] =
3323       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3324
3325   /* dynamically adjust player properties according to game engine version */
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327     game.initial_move_delay[i] =
3328       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3329        game.initial_move_delay_value[i] : 0);
3330
3331   /* ---------- initialize player's initial push delay --------------------- */
3332
3333   /* dynamically adjust player properties according to game engine version */
3334   game.initial_push_delay_value =
3335     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3336
3337   /* ---------- initialize changing elements ------------------------------- */
3338
3339   /* initialize changing elements information */
3340   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341   {
3342     struct ElementInfo *ei = &element_info[i];
3343
3344     /* this pointer might have been changed in the level editor */
3345     ei->change = &ei->change_page[0];
3346
3347     if (!IS_CUSTOM_ELEMENT(i))
3348     {
3349       ei->change->target_element = EL_EMPTY_SPACE;
3350       ei->change->delay_fixed = 0;
3351       ei->change->delay_random = 0;
3352       ei->change->delay_frames = 1;
3353     }
3354
3355     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3356     {
3357       ei->has_change_event[j] = FALSE;
3358
3359       ei->event_page_nr[j] = 0;
3360       ei->event_page[j] = &ei->change_page[0];
3361     }
3362   }
3363
3364   /* add changing elements from pre-defined list */
3365   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3366   {
3367     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3368     struct ElementInfo *ei = &element_info[ch_delay->element];
3369
3370     ei->change->target_element       = ch_delay->target_element;
3371     ei->change->delay_fixed          = ch_delay->change_delay;
3372
3373     ei->change->pre_change_function  = ch_delay->pre_change_function;
3374     ei->change->change_function      = ch_delay->change_function;
3375     ei->change->post_change_function = ch_delay->post_change_function;
3376
3377     ei->change->can_change = TRUE;
3378     ei->change->can_change_or_has_action = TRUE;
3379
3380     ei->has_change_event[CE_DELAY] = TRUE;
3381
3382     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3383     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3384   }
3385
3386   /* ---------- initialize internal run-time variables --------------------- */
3387
3388   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3389   {
3390     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3391
3392     for (j = 0; j < ei->num_change_pages; j++)
3393     {
3394       ei->change_page[j].can_change_or_has_action =
3395         (ei->change_page[j].can_change |
3396          ei->change_page[j].has_action);
3397     }
3398   }
3399
3400   /* add change events from custom element configuration */
3401   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3402   {
3403     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3404
3405     for (j = 0; j < ei->num_change_pages; j++)
3406     {
3407       if (!ei->change_page[j].can_change_or_has_action)
3408         continue;
3409
3410       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3411       {
3412         /* only add event page for the first page found with this event */
3413         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3414         {
3415           ei->has_change_event[k] = TRUE;
3416
3417           ei->event_page_nr[k] = j;
3418           ei->event_page[k] = &ei->change_page[j];
3419         }
3420       }
3421     }
3422   }
3423
3424 #if 1
3425   /* ---------- initialize reference elements in change conditions --------- */
3426
3427   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3428   {
3429     int element = EL_CUSTOM_START + i;
3430     struct ElementInfo *ei = &element_info[element];
3431
3432     for (j = 0; j < ei->num_change_pages; j++)
3433     {
3434       int trigger_element = ei->change_page[j].initial_trigger_element;
3435
3436       if (trigger_element >= EL_PREV_CE_8 &&
3437           trigger_element <= EL_NEXT_CE_8)
3438         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3439
3440       ei->change_page[j].trigger_element = trigger_element;
3441     }
3442   }
3443 #endif
3444
3445   /* ---------- initialize run-time trigger player and element ------------- */
3446
3447   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3448   {
3449     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3450
3451     for (j = 0; j < ei->num_change_pages; j++)
3452     {
3453       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3454       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3455       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3456       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3457       ei->change_page[j].actual_trigger_ce_value = 0;
3458       ei->change_page[j].actual_trigger_ce_score = 0;
3459     }
3460   }
3461
3462   /* ---------- initialize trigger events ---------------------------------- */
3463
3464   /* initialize trigger events information */
3465   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3466     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3467       trigger_events[i][j] = FALSE;
3468
3469   /* add trigger events from element change event properties */
3470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471   {
3472     struct ElementInfo *ei = &element_info[i];
3473
3474     for (j = 0; j < ei->num_change_pages; j++)
3475     {
3476       if (!ei->change_page[j].can_change_or_has_action)
3477         continue;
3478
3479       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3480       {
3481         int trigger_element = ei->change_page[j].trigger_element;
3482
3483         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3484         {
3485           if (ei->change_page[j].has_event[k])
3486           {
3487             if (IS_GROUP_ELEMENT(trigger_element))
3488             {
3489               struct ElementGroupInfo *group =
3490                 element_info[trigger_element].group;
3491
3492               for (l = 0; l < group->num_elements_resolved; l++)
3493                 trigger_events[group->element_resolved[l]][k] = TRUE;
3494             }
3495             else if (trigger_element == EL_ANY_ELEMENT)
3496               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3497                 trigger_events[l][k] = TRUE;
3498             else
3499               trigger_events[trigger_element][k] = TRUE;
3500           }
3501         }
3502       }
3503     }
3504   }
3505
3506   /* ---------- initialize push delay -------------------------------------- */
3507
3508   /* initialize push delay values to default */
3509   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3510   {
3511     if (!IS_CUSTOM_ELEMENT(i))
3512     {
3513       /* set default push delay values (corrected since version 3.0.7-1) */
3514       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3515       {
3516         element_info[i].push_delay_fixed = 2;
3517         element_info[i].push_delay_random = 8;
3518       }
3519       else
3520       {
3521         element_info[i].push_delay_fixed = 8;
3522         element_info[i].push_delay_random = 8;
3523       }
3524     }
3525   }
3526
3527   /* set push delay value for certain elements from pre-defined list */
3528   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3529   {
3530     int e = push_delay_list[i].element;
3531
3532     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3533     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3534   }
3535
3536   /* set push delay value for Supaplex elements for newer engine versions */
3537   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3538   {
3539     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540     {
3541       if (IS_SP_ELEMENT(i))
3542       {
3543         /* set SP push delay to just enough to push under a falling zonk */
3544         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3545
3546         element_info[i].push_delay_fixed  = delay;
3547         element_info[i].push_delay_random = 0;
3548       }
3549     }
3550   }
3551
3552   /* ---------- initialize move stepsize ----------------------------------- */
3553
3554   /* initialize move stepsize values to default */
3555   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3556     if (!IS_CUSTOM_ELEMENT(i))
3557       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3558
3559   /* set move stepsize value for certain elements from pre-defined list */
3560   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3561   {
3562     int e = move_stepsize_list[i].element;
3563
3564     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3565   }
3566
3567   /* ---------- initialize collect score ----------------------------------- */
3568
3569   /* initialize collect score values for custom elements from initial value */
3570   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3571     if (IS_CUSTOM_ELEMENT(i))
3572       element_info[i].collect_score = element_info[i].collect_score_initial;
3573
3574   /* ---------- initialize collect count ----------------------------------- */
3575
3576   /* initialize collect count values for non-custom elements */
3577   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578     if (!IS_CUSTOM_ELEMENT(i))
3579       element_info[i].collect_count_initial = 0;
3580
3581   /* add collect count values for all elements from pre-defined list */
3582   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3583     element_info[collect_count_list[i].element].collect_count_initial =
3584       collect_count_list[i].count;
3585
3586   /* ---------- initialize access direction -------------------------------- */
3587
3588   /* initialize access direction values to default (access from every side) */
3589   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3590     if (!IS_CUSTOM_ELEMENT(i))
3591       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3592
3593   /* set access direction value for certain elements from pre-defined list */
3594   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3595     element_info[access_direction_list[i].element].access_direction =
3596       access_direction_list[i].direction;
3597
3598   /* ---------- initialize explosion content ------------------------------- */
3599   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3600   {
3601     if (IS_CUSTOM_ELEMENT(i))
3602       continue;
3603
3604     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3605     {
3606       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3607
3608       element_info[i].content.e[x][y] =
3609         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3610          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3611          i == EL_PLAYER_3 ? EL_EMERALD :
3612          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3613          i == EL_MOLE ? EL_EMERALD_RED :
3614          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3615          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3616          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3617          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3618          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3619          i == EL_WALL_EMERALD ? EL_EMERALD :
3620          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3621          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3622          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3623          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3624          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3625          i == EL_WALL_PEARL ? EL_PEARL :
3626          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3627          EL_EMPTY);
3628     }
3629   }
3630
3631   /* ---------- initialize recursion detection ------------------------------ */
3632   recursion_loop_depth = 0;
3633   recursion_loop_detected = FALSE;
3634   recursion_loop_element = EL_UNDEFINED;
3635
3636   /* ---------- initialize graphics engine ---------------------------------- */
3637   game.scroll_delay_value =
3638     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3639      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3640   game.scroll_delay_value =
3641     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3642 }
3643
3644 int get_num_special_action(int element, int action_first, int action_last)
3645 {
3646   int num_special_action = 0;
3647   int i, j;
3648
3649   for (i = action_first; i <= action_last; i++)
3650   {
3651     boolean found = FALSE;
3652
3653     for (j = 0; j < NUM_DIRECTIONS; j++)
3654       if (el_act_dir2img(element, i, j) !=
3655           el_act_dir2img(element, ACTION_DEFAULT, j))
3656         found = TRUE;
3657
3658     if (found)
3659       num_special_action++;
3660     else
3661       break;
3662   }
3663
3664   return num_special_action;
3665 }
3666
3667
3668 /*
3669   =============================================================================
3670   InitGame()
3671   -----------------------------------------------------------------------------
3672   initialize and start new game
3673   =============================================================================
3674 */
3675
3676 void InitGame()
3677 {
3678   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3679   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3680   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3681 #if 0
3682   boolean do_fading = (game_status == GAME_MODE_MAIN);
3683 #endif
3684 #if 1
3685   int initial_move_dir = MV_DOWN;
3686 #else
3687   int initial_move_dir = MV_NONE;
3688 #endif
3689   int i, j, x, y;
3690
3691   game_status = GAME_MODE_PLAYING;
3692
3693   InitGameEngine();
3694   InitGameControlValues();
3695
3696   /* don't play tapes over network */
3697   network_playing = (options.network && !tape.playing);
3698
3699   for (i = 0; i < MAX_PLAYERS; i++)
3700   {
3701     struct PlayerInfo *player = &stored_player[i];
3702
3703     player->index_nr = i;
3704     player->index_bit = (1 << i);
3705     player->element_nr = EL_PLAYER_1 + i;
3706
3707     player->present = FALSE;
3708     player->active = FALSE;
3709     player->killed = FALSE;
3710     player->reanimated = FALSE;
3711
3712     player->action = 0;
3713     player->effective_action = 0;
3714     player->programmed_action = 0;
3715
3716     player->score = 0;
3717     player->score_final = 0;
3718
3719     player->gems_still_needed = level.gems_needed;
3720     player->sokobanfields_still_needed = 0;
3721     player->lights_still_needed = 0;
3722     player->friends_still_needed = 0;
3723
3724     for (j = 0; j < MAX_NUM_KEYS; j++)
3725       player->key[j] = FALSE;
3726
3727     player->num_white_keys = 0;
3728
3729     player->dynabomb_count = 0;
3730     player->dynabomb_size = 1;
3731     player->dynabombs_left = 0;
3732     player->dynabomb_xl = FALSE;
3733
3734     player->MovDir = initial_move_dir;
3735     player->MovPos = 0;
3736     player->GfxPos = 0;
3737     player->GfxDir = initial_move_dir;
3738     player->GfxAction = ACTION_DEFAULT;
3739     player->Frame = 0;
3740     player->StepFrame = 0;
3741
3742     player->initial_element = player->element_nr;
3743     player->artwork_element =
3744       (level.use_artwork_element[i] ? level.artwork_element[i] :
3745        player->element_nr);
3746     player->use_murphy = FALSE;
3747
3748     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3749     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3750
3751     player->gravity = level.initial_player_gravity[i];
3752
3753     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3754
3755     player->actual_frame_counter = 0;
3756
3757     player->step_counter = 0;
3758
3759     player->last_move_dir = initial_move_dir;
3760
3761     player->is_active = FALSE;
3762
3763     player->is_waiting = FALSE;
3764     player->is_moving = FALSE;
3765     player->is_auto_moving = FALSE;
3766     player->is_digging = FALSE;
3767     player->is_snapping = FALSE;
3768     player->is_collecting = FALSE;
3769     player->is_pushing = FALSE;
3770     player->is_switching = FALSE;
3771     player->is_dropping = FALSE;
3772     player->is_dropping_pressed = FALSE;
3773
3774     player->is_bored = FALSE;
3775     player->is_sleeping = FALSE;
3776
3777     player->frame_counter_bored = -1;
3778     player->frame_counter_sleeping = -1;
3779
3780     player->anim_delay_counter = 0;
3781     player->post_delay_counter = 0;
3782
3783     player->dir_waiting = initial_move_dir;
3784     player->action_waiting = ACTION_DEFAULT;
3785     player->last_action_waiting = ACTION_DEFAULT;
3786     player->special_action_bored = ACTION_DEFAULT;
3787     player->special_action_sleeping = ACTION_DEFAULT;
3788
3789     player->switch_x = -1;
3790     player->switch_y = -1;
3791
3792     player->drop_x = -1;
3793     player->drop_y = -1;
3794
3795     player->show_envelope = 0;
3796
3797     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3798
3799     player->push_delay       = -1;      /* initialized when pushing starts */
3800     player->push_delay_value = game.initial_push_delay_value;
3801
3802     player->drop_delay = 0;
3803     player->drop_pressed_delay = 0;
3804
3805     player->last_jx = -1;
3806     player->last_jy = -1;
3807     player->jx = -1;
3808     player->jy = -1;
3809
3810     player->shield_normal_time_left = 0;
3811     player->shield_deadly_time_left = 0;
3812
3813     player->inventory_infinite_element = EL_UNDEFINED;
3814     player->inventory_size = 0;
3815
3816     if (level.use_initial_inventory[i])
3817     {
3818       for (j = 0; j < level.initial_inventory_size[i]; j++)
3819       {
3820         int element = level.initial_inventory_content[i][j];
3821         int collect_count = element_info[element].collect_count_initial;
3822         int k;
3823
3824         if (!IS_CUSTOM_ELEMENT(element))
3825           collect_count = 1;
3826
3827         if (collect_count == 0)
3828           player->inventory_infinite_element = element;
3829         else
3830           for (k = 0; k < collect_count; k++)
3831             if (player->inventory_size < MAX_INVENTORY_SIZE)
3832               player->inventory_element[player->inventory_size++] = element;
3833       }
3834     }
3835
3836     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3837     SnapField(player, 0, 0);
3838
3839     player->LevelSolved = FALSE;
3840     player->GameOver = FALSE;
3841
3842     player->LevelSolved_GameWon = FALSE;
3843     player->LevelSolved_GameEnd = FALSE;
3844     player->LevelSolved_PanelOff = FALSE;
3845     player->LevelSolved_SaveTape = FALSE;
3846     player->LevelSolved_SaveScore = FALSE;
3847     player->LevelSolved_CountingTime = 0;
3848     player->LevelSolved_CountingScore = 0;
3849   }
3850
3851   network_player_action_received = FALSE;
3852
3853 #if defined(NETWORK_AVALIABLE)
3854   /* initial null action */
3855   if (network_playing)
3856     SendToServer_MovePlayer(MV_NONE);
3857 #endif
3858
3859   ZX = ZY = -1;
3860   ExitX = ExitY = -1;
3861
3862   FrameCounter = 0;
3863   TimeFrames = 0;
3864   TimePlayed = 0;
3865   TimeLeft = level.time;
3866   TapeTime = 0;
3867
3868   ScreenMovDir = MV_NONE;
3869   ScreenMovPos = 0;
3870   ScreenGfxPos = 0;
3871
3872   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3873
3874   AllPlayersGone = FALSE;
3875
3876   game.yamyam_content_nr = 0;
3877   game.robot_wheel_active = FALSE;
3878   game.magic_wall_active = FALSE;
3879   game.magic_wall_time_left = 0;
3880   game.light_time_left = 0;
3881   game.timegate_time_left = 0;
3882   game.switchgate_pos = 0;
3883   game.wind_direction = level.wind_direction_initial;
3884
3885 #if !USE_PLAYER_GRAVITY
3886   game.gravity = FALSE;
3887   game.explosions_delayed = TRUE;
3888 #endif
3889
3890   game.lenses_time_left = 0;
3891   game.magnify_time_left = 0;
3892
3893   game.ball_state = level.ball_state_initial;
3894   game.ball_content_nr = 0;
3895
3896   game.envelope_active = FALSE;
3897
3898   /* set focus to local player for network games, else to all players */
3899   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3900   game.centered_player_nr_next = game.centered_player_nr;
3901   game.set_centered_player = FALSE;
3902
3903   if (network_playing && tape.recording)
3904   {
3905     /* store client dependent player focus when recording network games */
3906     tape.centered_player_nr_next = game.centered_player_nr_next;
3907     tape.set_centered_player = TRUE;
3908   }
3909
3910   for (i = 0; i < NUM_BELTS; i++)
3911   {
3912     game.belt_dir[i] = MV_NONE;
3913     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3914   }
3915
3916   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3917     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3918
3919   SCAN_PLAYFIELD(x, y)
3920   {
3921     Feld[x][y] = level.field[x][y];
3922     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3923     ChangeDelay[x][y] = 0;
3924     ChangePage[x][y] = -1;
3925 #if USE_NEW_CUSTOM_VALUE
3926     CustomValue[x][y] = 0;              /* initialized in InitField() */
3927 #endif
3928     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3929     AmoebaNr[x][y] = 0;
3930     WasJustMoving[x][y] = 0;
3931     WasJustFalling[x][y] = 0;
3932     CheckCollision[x][y] = 0;
3933     CheckImpact[x][y] = 0;
3934     Stop[x][y] = FALSE;
3935     Pushed[x][y] = FALSE;
3936
3937     ChangeCount[x][y] = 0;
3938     ChangeEvent[x][y] = -1;
3939
3940     ExplodePhase[x][y] = 0;
3941     ExplodeDelay[x][y] = 0;
3942     ExplodeField[x][y] = EX_TYPE_NONE;
3943
3944     RunnerVisit[x][y] = 0;
3945     PlayerVisit[x][y] = 0;
3946
3947     GfxFrame[x][y] = 0;
3948     GfxRandom[x][y] = INIT_GFX_RANDOM();
3949     GfxElement[x][y] = EL_UNDEFINED;
3950     GfxAction[x][y] = ACTION_DEFAULT;
3951     GfxDir[x][y] = MV_NONE;
3952     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3953   }
3954
3955   SCAN_PLAYFIELD(x, y)
3956   {
3957     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3958       emulate_bd = FALSE;
3959     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3960       emulate_sb = FALSE;
3961     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3962       emulate_sp = FALSE;
3963
3964     InitField(x, y, TRUE);
3965
3966     ResetGfxAnimation(x, y);
3967   }
3968
3969   InitBeltMovement();
3970
3971   for (i = 0; i < MAX_PLAYERS; i++)
3972   {
3973     struct PlayerInfo *player = &stored_player[i];
3974
3975     /* set number of special actions for bored and sleeping animation */
3976     player->num_special_action_bored =
3977       get_num_special_action(player->artwork_element,
3978                              ACTION_BORING_1, ACTION_BORING_LAST);
3979     player->num_special_action_sleeping =
3980       get_num_special_action(player->artwork_element,
3981                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3982   }
3983
3984   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3985                     emulate_sb ? EMU_SOKOBAN :
3986                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3987
3988 #if USE_NEW_ALL_SLIPPERY
3989   /* initialize type of slippery elements */
3990   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3991   {
3992     if (!IS_CUSTOM_ELEMENT(i))
3993     {
3994       /* default: elements slip down either to the left or right randomly */
3995       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3996
3997       /* SP style elements prefer to slip down on the left side */
3998       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3999         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4000
4001       /* BD style elements prefer to slip down on the left side */
4002       if (game.emulation == EMU_BOULDERDASH)
4003         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4004     }
4005   }
4006 #endif
4007
4008   /* initialize explosion and ignition delay */
4009   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4010   {
4011     if (!IS_CUSTOM_ELEMENT(i))
4012     {
4013       int num_phase = 8;
4014       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4015                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4016                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4017       int last_phase = (num_phase + 1) * delay;
4018       int half_phase = (num_phase / 2) * delay;
4019
4020       element_info[i].explosion_delay = last_phase - 1;
4021       element_info[i].ignition_delay = half_phase;
4022
4023       if (i == EL_BLACK_ORB)
4024         element_info[i].ignition_delay = 1;
4025     }
4026
4027 #if 0
4028     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4029       element_info[i].explosion_delay = 1;
4030
4031     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4032       element_info[i].ignition_delay = 1;
4033 #endif
4034   }
4035
4036   /* correct non-moving belts to start moving left */
4037   for (i = 0; i < NUM_BELTS; i++)
4038     if (game.belt_dir[i] == MV_NONE)
4039       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4040
4041   /* check if any connected player was not found in playfield */
4042   for (i = 0; i < MAX_PLAYERS; i++)
4043   {
4044     struct PlayerInfo *player = &stored_player[i];
4045
4046     if (player->connected && !player->present)
4047     {
4048       for (j = 0; j < MAX_PLAYERS; j++)
4049       {
4050         struct PlayerInfo *some_player = &stored_player[j];
4051         int jx = some_player->jx, jy = some_player->jy;
4052
4053         /* assign first free player found that is present in the playfield */
4054         if (some_player->present && !some_player->connected)
4055         {
4056           player->present = TRUE;
4057           player->active = TRUE;
4058
4059           some_player->present = FALSE;
4060           some_player->active = FALSE;
4061
4062           player->initial_element = some_player->initial_element;
4063           player->artwork_element = some_player->artwork_element;
4064
4065           player->block_last_field       = some_player->block_last_field;
4066           player->block_delay_adjustment = some_player->block_delay_adjustment;
4067
4068           StorePlayer[jx][jy] = player->element_nr;
4069           player->jx = player->last_jx = jx;
4070           player->jy = player->last_jy = jy;
4071
4072           break;
4073         }
4074       }
4075     }
4076   }
4077
4078   if (tape.playing)
4079   {
4080     /* when playing a tape, eliminate all players who do not participate */
4081
4082     for (i = 0; i < MAX_PLAYERS; i++)
4083     {
4084       if (stored_player[i].active && !tape.player_participates[i])
4085       {
4086         struct PlayerInfo *player = &stored_player[i];
4087         int jx = player->jx, jy = player->jy;
4088
4089         player->active = FALSE;
4090         StorePlayer[jx][jy] = 0;
4091         Feld[jx][jy] = EL_EMPTY;
4092       }
4093     }
4094   }
4095   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4096   {
4097     /* when in single player mode, eliminate all but the first active player */
4098
4099     for (i = 0; i < MAX_PLAYERS; i++)
4100     {
4101       if (stored_player[i].active)
4102       {
4103         for (j = i + 1; j < MAX_PLAYERS; j++)
4104         {
4105           if (stored_player[j].active)
4106           {
4107             struct PlayerInfo *player = &stored_player[j];
4108             int jx = player->jx, jy = player->jy;
4109
4110             player->active = FALSE;
4111             player->present = FALSE;
4112
4113             StorePlayer[jx][jy] = 0;
4114             Feld[jx][jy] = EL_EMPTY;
4115           }
4116         }
4117       }
4118     }
4119   }
4120
4121   /* when recording the game, store which players take part in the game */
4122   if (tape.recording)
4123   {
4124     for (i = 0; i < MAX_PLAYERS; i++)
4125       if (stored_player[i].active)
4126         tape.player_participates[i] = TRUE;
4127   }
4128
4129   if (options.debug)
4130   {
4131     for (i = 0; i < MAX_PLAYERS; i++)
4132     {
4133       struct PlayerInfo *player = &stored_player[i];
4134
4135       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4136              i+1,
4137              player->present,
4138              player->connected,
4139              player->active);
4140       if (local_player == player)
4141         printf("Player  %d is local player.\n", i+1);
4142     }
4143   }
4144
4145   if (BorderElement == EL_EMPTY)
4146   {
4147     SBX_Left = 0;
4148     SBX_Right = lev_fieldx - SCR_FIELDX;
4149     SBY_Upper = 0;
4150     SBY_Lower = lev_fieldy - SCR_FIELDY;
4151   }
4152   else
4153   {
4154     SBX_Left = -1;
4155     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4156     SBY_Upper = -1;
4157     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4158   }
4159
4160   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4161     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4162
4163   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4164     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4165
4166   /* if local player not found, look for custom element that might create
4167      the player (make some assumptions about the right custom element) */
4168   if (!local_player->present)
4169   {
4170     int start_x = 0, start_y = 0;
4171     int found_rating = 0;
4172     int found_element = EL_UNDEFINED;
4173     int player_nr = local_player->index_nr;
4174
4175     SCAN_PLAYFIELD(x, y)
4176     {
4177       int element = Feld[x][y];
4178       int content;
4179       int xx, yy;
4180       boolean is_player;
4181
4182       if (level.use_start_element[player_nr] &&
4183           level.start_element[player_nr] == element &&
4184           found_rating < 4)
4185       {
4186         start_x = x;
4187         start_y = y;
4188
4189         found_rating = 4;
4190         found_element = element;
4191       }
4192
4193       if (!IS_CUSTOM_ELEMENT(element))
4194         continue;
4195
4196       if (CAN_CHANGE(element))
4197       {
4198         for (i = 0; i < element_info[element].num_change_pages; i++)
4199         {
4200           /* check for player created from custom element as single target */
4201           content = element_info[element].change_page[i].target_element;
4202           is_player = ELEM_IS_PLAYER(content);
4203
4204           if (is_player && (found_rating < 3 ||
4205                             (found_rating == 3 && element < found_element)))
4206           {
4207             start_x = x;
4208             start_y = y;
4209
4210             found_rating = 3;
4211             found_element = element;
4212           }
4213         }
4214       }
4215
4216       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4217       {
4218         /* check for player created from custom element as explosion content */
4219         content = element_info[element].content.e[xx][yy];
4220         is_player = ELEM_IS_PLAYER(content);
4221
4222         if (is_player && (found_rating < 2 ||
4223                           (found_rating == 2 && element < found_element)))
4224         {
4225           start_x = x + xx - 1;
4226           start_y = y + yy - 1;
4227
4228           found_rating = 2;
4229           found_element = element;
4230         }
4231
4232         if (!CAN_CHANGE(element))
4233           continue;
4234
4235         for (i = 0; i < element_info[element].num_change_pages; i++)
4236         {
4237           /* check for player created from custom element as extended target */
4238           content =
4239             element_info[element].change_page[i].target_content.e[xx][yy];
4240
4241           is_player = ELEM_IS_PLAYER(content);
4242
4243           if (is_player && (found_rating < 1 ||
4244                             (found_rating == 1 && element < found_element)))
4245           {
4246             start_x = x + xx - 1;
4247             start_y = y + yy - 1;
4248
4249             found_rating = 1;
4250             found_element = element;
4251           }
4252         }
4253       }
4254     }
4255
4256     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4257                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4258                 start_x - MIDPOSX);
4259
4260     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4262                 start_y - MIDPOSY);
4263   }
4264   else
4265   {
4266     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4267                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268                 local_player->jx - MIDPOSX);
4269
4270     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272                 local_player->jy - MIDPOSY);
4273   }
4274
4275 #if 0
4276   /* do not use PLAYING mask for fading out from main screen */
4277   game_status = GAME_MODE_MAIN;
4278 #endif
4279
4280   StopAnimation();
4281
4282   if (!game.restart_level)
4283     CloseDoor(DOOR_CLOSE_1);
4284
4285 #if 1
4286   if (level_editor_test_game)
4287     FadeSkipNextFadeIn();
4288   else
4289     FadeSetEnterScreen();
4290 #else
4291   if (level_editor_test_game)
4292     fading = fading_none;
4293   else
4294     fading = menu.destination;
4295 #endif
4296
4297 #if 1
4298   FadeOut(REDRAW_FIELD);
4299 #else
4300   if (do_fading)
4301     FadeOut(REDRAW_FIELD);
4302 #endif
4303
4304 #if 0
4305   game_status = GAME_MODE_PLAYING;
4306 #endif
4307
4308   /* !!! FIX THIS (START) !!! */
4309   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4310   {
4311     InitGameEngine_EM();
4312
4313     /* blit playfield from scroll buffer to normal back buffer for fading in */
4314     BlitScreenToBitmap_EM(backbuffer);
4315   }
4316   else
4317   {
4318     DrawLevel();
4319     DrawAllPlayers();
4320
4321     /* after drawing the level, correct some elements */
4322     if (game.timegate_time_left == 0)
4323       CloseAllOpenTimegates();
4324
4325     /* blit playfield from scroll buffer to normal back buffer for fading in */
4326     if (setup.soft_scrolling)
4327       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4328
4329     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4330   }
4331   /* !!! FIX THIS (END) !!! */
4332
4333 #if 1
4334   FadeIn(REDRAW_FIELD);
4335 #else
4336   if (do_fading)
4337     FadeIn(REDRAW_FIELD);
4338
4339   BackToFront();
4340 #endif
4341
4342   if (!game.restart_level)
4343   {
4344     /* copy default game door content to main double buffer */
4345     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4346                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4347   }
4348
4349   SetPanelBackground();
4350   SetDrawBackgroundMask(REDRAW_DOOR_1);
4351
4352 #if 1
4353   UpdateAndDisplayGameControlValues();
4354 #else
4355   UpdateGameDoorValues();
4356   DrawGameDoorValues();
4357 #endif
4358
4359   if (!game.restart_level)
4360   {
4361     UnmapGameButtons();
4362     UnmapTapeButtons();
4363     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4364     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4365     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4366     MapGameButtons();
4367     MapTapeButtons();
4368
4369     /* copy actual game door content to door double buffer for OpenDoor() */
4370     BlitBitmap(drawto, bitmap_db_door,
4371                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4372
4373     OpenDoor(DOOR_OPEN_ALL);
4374
4375     PlaySound(SND_GAME_STARTING);
4376
4377     if (setup.sound_music)
4378       PlayLevelMusic();
4379
4380     KeyboardAutoRepeatOffUnlessAutoplay();
4381
4382     if (options.debug)
4383     {
4384       for (i = 0; i < MAX_PLAYERS; i++)
4385         printf("Player %d %sactive.\n",
4386                i + 1, (stored_player[i].active ? "" : "not "));
4387     }
4388   }
4389
4390 #if 1
4391   UnmapAllGadgets();
4392
4393   MapGameButtons();
4394   MapTapeButtons();
4395 #endif
4396
4397   game.restart_level = FALSE;
4398 }
4399
4400 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4401 {
4402   /* this is used for non-R'n'D game engines to update certain engine values */
4403
4404   /* needed to determine if sounds are played within the visible screen area */
4405   scroll_x = actual_scroll_x;
4406   scroll_y = actual_scroll_y;
4407 }
4408
4409 void InitMovDir(int x, int y)
4410 {
4411   int i, element = Feld[x][y];
4412   static int xy[4][2] =
4413   {
4414     {  0, +1 },
4415     { +1,  0 },
4416     {  0, -1 },
4417     { -1,  0 }
4418   };
4419   static int direction[3][4] =
4420   {
4421     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4422     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4423     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4424   };
4425
4426   switch (element)
4427   {
4428     case EL_BUG_RIGHT:
4429     case EL_BUG_UP:
4430     case EL_BUG_LEFT:
4431     case EL_BUG_DOWN:
4432       Feld[x][y] = EL_BUG;
4433       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4434       break;
4435
4436     case EL_SPACESHIP_RIGHT:
4437     case EL_SPACESHIP_UP:
4438     case EL_SPACESHIP_LEFT:
4439     case EL_SPACESHIP_DOWN:
4440       Feld[x][y] = EL_SPACESHIP;
4441       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4442       break;
4443
4444     case EL_BD_BUTTERFLY_RIGHT:
4445     case EL_BD_BUTTERFLY_UP:
4446     case EL_BD_BUTTERFLY_LEFT:
4447     case EL_BD_BUTTERFLY_DOWN:
4448       Feld[x][y] = EL_BD_BUTTERFLY;
4449       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4450       break;
4451
4452     case EL_BD_FIREFLY_RIGHT:
4453     case EL_BD_FIREFLY_UP:
4454     case EL_BD_FIREFLY_LEFT:
4455     case EL_BD_FIREFLY_DOWN:
4456       Feld[x][y] = EL_BD_FIREFLY;
4457       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4458       break;
4459
4460     case EL_PACMAN_RIGHT:
4461     case EL_PACMAN_UP:
4462     case EL_PACMAN_LEFT:
4463     case EL_PACMAN_DOWN:
4464       Feld[x][y] = EL_PACMAN;
4465       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4466       break;
4467
4468     case EL_YAMYAM_LEFT:
4469     case EL_YAMYAM_RIGHT:
4470     case EL_YAMYAM_UP:
4471     case EL_YAMYAM_DOWN:
4472       Feld[x][y] = EL_YAMYAM;
4473       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4474       break;
4475
4476     case EL_SP_SNIKSNAK:
4477       MovDir[x][y] = MV_UP;
4478       break;
4479
4480     case EL_SP_ELECTRON:
4481       MovDir[x][y] = MV_LEFT;
4482       break;
4483
4484     case EL_MOLE_LEFT:
4485     case EL_MOLE_RIGHT:
4486     case EL_MOLE_UP:
4487     case EL_MOLE_DOWN:
4488       Feld[x][y] = EL_MOLE;
4489       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4490       break;
4491
4492     default:
4493       if (IS_CUSTOM_ELEMENT(element))
4494       {
4495         struct ElementInfo *ei = &element_info[element];
4496         int move_direction_initial = ei->move_direction_initial;
4497         int move_pattern = ei->move_pattern;
4498
4499         if (move_direction_initial == MV_START_PREVIOUS)
4500         {
4501           if (MovDir[x][y] != MV_NONE)
4502             return;
4503
4504           move_direction_initial = MV_START_AUTOMATIC;
4505         }
4506
4507         if (move_direction_initial == MV_START_RANDOM)
4508           MovDir[x][y] = 1 << RND(4);
4509         else if (move_direction_initial & MV_ANY_DIRECTION)
4510           MovDir[x][y] = move_direction_initial;
4511         else if (move_pattern == MV_ALL_DIRECTIONS ||
4512                  move_pattern == MV_TURNING_LEFT ||
4513                  move_pattern == MV_TURNING_RIGHT ||
4514                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4515                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4516                  move_pattern == MV_TURNING_RANDOM)
4517           MovDir[x][y] = 1 << RND(4);
4518         else if (move_pattern == MV_HORIZONTAL)
4519           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4520         else if (move_pattern == MV_VERTICAL)
4521           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4522         else if (move_pattern & MV_ANY_DIRECTION)
4523           MovDir[x][y] = element_info[element].move_pattern;
4524         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4525                  move_pattern == MV_ALONG_RIGHT_SIDE)
4526         {
4527           /* use random direction as default start direction */
4528           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4529             MovDir[x][y] = 1 << RND(4);
4530
4531           for (i = 0; i < NUM_DIRECTIONS; i++)
4532           {
4533             int x1 = x + xy[i][0];
4534             int y1 = y + xy[i][1];
4535
4536             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4537             {
4538               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4539                 MovDir[x][y] = direction[0][i];
4540               else
4541                 MovDir[x][y] = direction[1][i];
4542
4543               break;
4544             }
4545           }
4546         }                
4547       }
4548       else
4549       {
4550         MovDir[x][y] = 1 << RND(4);
4551
4552         if (element != EL_BUG &&
4553             element != EL_SPACESHIP &&
4554             element != EL_BD_BUTTERFLY &&
4555             element != EL_BD_FIREFLY)
4556           break;
4557
4558         for (i = 0; i < NUM_DIRECTIONS; i++)
4559         {
4560           int x1 = x + xy[i][0];
4561           int y1 = y + xy[i][1];
4562
4563           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4564           {
4565             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4566             {
4567               MovDir[x][y] = direction[0][i];
4568               break;
4569             }
4570             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4571                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4572             {
4573               MovDir[x][y] = direction[1][i];
4574               break;
4575             }
4576           }
4577         }
4578       }
4579       break;
4580   }
4581
4582   GfxDir[x][y] = MovDir[x][y];
4583 }
4584
4585 void InitAmoebaNr(int x, int y)
4586 {
4587   int i;
4588   int group_nr = AmoebeNachbarNr(x, y);
4589
4590   if (group_nr == 0)
4591   {
4592     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4593     {
4594       if (AmoebaCnt[i] == 0)
4595       {
4596         group_nr = i;
4597         break;
4598       }
4599     }
4600   }
4601
4602   AmoebaNr[x][y] = group_nr;
4603   AmoebaCnt[group_nr]++;
4604   AmoebaCnt2[group_nr]++;
4605 }
4606
4607 static void PlayerWins(struct PlayerInfo *player)
4608 {
4609   player->LevelSolved = TRUE;
4610   player->GameOver = TRUE;
4611
4612   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4613                          level.native_em_level->lev->score : player->score);
4614
4615   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4616   player->LevelSolved_CountingScore = player->score_final;
4617 }
4618
4619 void GameWon()
4620 {
4621   static int time, time_final;
4622   static int score, score_final;
4623   static int game_over_delay_1 = 0;
4624   static int game_over_delay_2 = 0;
4625   int game_over_delay_value_1 = 50;
4626   int game_over_delay_value_2 = 50;
4627
4628   if (!local_player->LevelSolved_GameWon)
4629   {
4630     int i;
4631
4632     /* do not start end game actions before the player stops moving (to exit) */
4633     if (local_player->MovPos)
4634       return;
4635
4636     local_player->LevelSolved_GameWon = TRUE;
4637     local_player->LevelSolved_SaveTape = tape.recording;
4638     local_player->LevelSolved_SaveScore = !tape.playing;
4639
4640     if (tape.auto_play)         /* tape might already be stopped here */
4641       tape.auto_play_level_solved = TRUE;
4642
4643 #if 1
4644     TapeStop();
4645 #endif
4646
4647     game_over_delay_1 = game_over_delay_value_1;
4648     game_over_delay_2 = game_over_delay_value_2;
4649
4650     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4651     score = score_final = local_player->score_final;
4652
4653     if (TimeLeft > 0)
4654     {
4655       time_final = 0;
4656       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4657     }
4658     else if (level.time == 0 && TimePlayed < 999)
4659     {
4660       time_final = 999;
4661       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4662     }
4663
4664     local_player->score_final = score_final;
4665
4666     if (level_editor_test_game)
4667     {
4668       time = time_final;
4669       score = score_final;
4670
4671 #if 1
4672       local_player->LevelSolved_CountingTime = time;
4673       local_player->LevelSolved_CountingScore = score;
4674
4675       game_panel_controls[GAME_PANEL_TIME].value = time;
4676       game_panel_controls[GAME_PANEL_SCORE].value = score;
4677
4678       DisplayGameControlValues();
4679 #else
4680       DrawGameValue_Time(time);
4681       DrawGameValue_Score(score);
4682 #endif
4683     }
4684
4685     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4686     {
4687       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4688       {
4689         /* close exit door after last player */
4690         if ((AllPlayersGone &&
4691              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4692               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4693               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4694             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4695             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4696         {
4697           int element = Feld[ExitX][ExitY];
4698
4699 #if 0
4700           if (element == EL_EM_EXIT_OPEN ||
4701               element == EL_EM_STEEL_EXIT_OPEN)
4702           {
4703             Bang(ExitX, ExitY);
4704           }
4705           else
4706 #endif
4707           {
4708             Feld[ExitX][ExitY] =
4709               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4710                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4711                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4712                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4713                EL_EM_STEEL_EXIT_CLOSING);
4714
4715             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4716           }
4717         }
4718
4719         /* player disappears */
4720         DrawLevelField(ExitX, ExitY);
4721       }
4722
4723       for (i = 0; i < MAX_PLAYERS; i++)
4724       {
4725         struct PlayerInfo *player = &stored_player[i];
4726
4727         if (player->present)
4728         {
4729           RemovePlayer(player);
4730
4731           /* player disappears */
4732           DrawLevelField(player->jx, player->jy);
4733         }
4734       }
4735     }
4736
4737     PlaySound(SND_GAME_WINNING);
4738   }
4739
4740   if (game_over_delay_1 > 0)
4741   {
4742     game_over_delay_1--;
4743
4744     return;
4745   }
4746
4747   if (time != time_final)
4748   {
4749     int time_to_go = ABS(time_final - time);
4750     int time_count_dir = (time < time_final ? +1 : -1);
4751     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4752
4753     time  += time_count_steps * time_count_dir;
4754     score += time_count_steps * level.score[SC_TIME_BONUS];
4755
4756 #if 1
4757     local_player->LevelSolved_CountingTime = time;
4758     local_player->LevelSolved_CountingScore = score;
4759
4760     game_panel_controls[GAME_PANEL_TIME].value = time;
4761     game_panel_controls[GAME_PANEL_SCORE].value = score;
4762
4763     DisplayGameControlValues();
4764 #else
4765     DrawGameValue_Time(time);
4766     DrawGameValue_Score(score);
4767 #endif
4768
4769     if (time == time_final)
4770       StopSound(SND_GAME_LEVELTIME_BONUS);
4771     else if (setup.sound_loops)
4772       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4773     else
4774       PlaySound(SND_GAME_LEVELTIME_BONUS);
4775
4776     return;
4777   }
4778
4779   local_player->LevelSolved_PanelOff = TRUE;
4780
4781   if (game_over_delay_2 > 0)
4782   {
4783     game_over_delay_2--;
4784
4785     return;
4786   }
4787
4788 #if 1
4789   GameEnd();
4790 #endif
4791 }
4792
4793 void GameEnd()
4794 {
4795   int hi_pos;
4796   boolean raise_level = FALSE;
4797
4798   local_player->LevelSolved_GameEnd = TRUE;
4799
4800   CloseDoor(DOOR_CLOSE_1);
4801
4802   if (local_player->LevelSolved_SaveTape)
4803   {
4804 #if 0
4805     TapeStop();
4806 #endif
4807
4808 #if 1
4809     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4810 #else
4811     SaveTape(tape.level_nr);            /* ask to save tape */
4812 #endif
4813   }
4814
4815   if (level_editor_test_game)
4816   {
4817     game_status = GAME_MODE_MAIN;
4818
4819 #if 1
4820     DrawAndFadeInMainMenu(REDRAW_FIELD);
4821 #else
4822     DrawMainMenu();
4823 #endif
4824
4825     return;
4826   }
4827
4828   if (!local_player->LevelSolved_SaveScore)
4829   {
4830 #if 1
4831     FadeOut(REDRAW_FIELD);
4832 #endif
4833
4834     game_status = GAME_MODE_MAIN;
4835
4836     DrawAndFadeInMainMenu(REDRAW_FIELD);
4837
4838     return;
4839   }
4840
4841   if (level_nr == leveldir_current->handicap_level)
4842   {
4843     leveldir_current->handicap_level++;
4844     SaveLevelSetup_SeriesInfo();
4845   }
4846
4847   if (level_nr < leveldir_current->last_level)
4848     raise_level = TRUE;                 /* advance to next level */
4849
4850   if ((hi_pos = NewHiScore()) >= 0) 
4851   {
4852     game_status = GAME_MODE_SCORES;
4853
4854     DrawHallOfFame(hi_pos);
4855
4856     if (raise_level)
4857     {
4858       level_nr++;
4859       TapeErase();
4860     }
4861   }
4862   else
4863   {
4864 #if 1
4865     FadeOut(REDRAW_FIELD);
4866 #endif
4867
4868     game_status = GAME_MODE_MAIN;
4869
4870     if (raise_level)
4871     {
4872       level_nr++;
4873       TapeErase();
4874     }
4875
4876     DrawAndFadeInMainMenu(REDRAW_FIELD);
4877   }
4878 }
4879
4880 int NewHiScore()
4881 {
4882   int k, l;
4883   int position = -1;
4884
4885   LoadScore(level_nr);
4886
4887   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4888       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4889     return -1;
4890
4891   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4892   {
4893     if (local_player->score_final > highscore[k].Score)
4894     {
4895       /* player has made it to the hall of fame */
4896
4897       if (k < MAX_SCORE_ENTRIES - 1)
4898       {
4899         int m = MAX_SCORE_ENTRIES - 1;
4900
4901 #ifdef ONE_PER_NAME
4902         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4903           if (strEqual(setup.player_name, highscore[l].Name))
4904             m = l;
4905         if (m == k)     /* player's new highscore overwrites his old one */
4906           goto put_into_list;
4907 #endif
4908
4909         for (l = m; l > k; l--)
4910         {
4911           strcpy(highscore[l].Name, highscore[l - 1].Name);
4912           highscore[l].Score = highscore[l - 1].Score;
4913         }
4914       }
4915
4916 #ifdef ONE_PER_NAME
4917       put_into_list:
4918 #endif
4919       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4920       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4921       highscore[k].Score = local_player->score_final; 
4922       position = k;
4923       break;
4924     }
4925
4926 #ifdef ONE_PER_NAME
4927     else if (!strncmp(setup.player_name, highscore[k].Name,
4928                       MAX_PLAYER_NAME_LEN))
4929       break;    /* player already there with a higher score */
4930 #endif
4931
4932   }
4933
4934   if (position >= 0) 
4935     SaveScore(level_nr);
4936
4937   return position;
4938 }
4939
4940 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4941 {
4942   int element = Feld[x][y];
4943   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4944   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4945   int horiz_move = (dx != 0);
4946   int sign = (horiz_move ? dx : dy);
4947   int step = sign * element_info[element].move_stepsize;
4948
4949   /* special values for move stepsize for spring and things on conveyor belt */
4950   if (horiz_move)
4951   {
4952     if (CAN_FALL(element) &&
4953         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4954       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4955     else if (element == EL_SPRING)
4956       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4957   }
4958
4959   return step;
4960 }
4961
4962 inline static int getElementMoveStepsize(int x, int y)
4963 {
4964   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4965 }
4966
4967 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4968 {
4969   if (player->GfxAction != action || player->GfxDir != dir)
4970   {
4971 #if 0
4972     printf("Player frame reset! (%d => %d, %d => %d)\n",
4973            player->GfxAction, action, player->GfxDir, dir);
4974 #endif
4975
4976     player->GfxAction = action;
4977     player->GfxDir = dir;
4978     player->Frame = 0;
4979     player->StepFrame = 0;
4980   }
4981 }
4982
4983 #if USE_GFX_RESET_GFX_ANIMATION
4984 static void ResetGfxFrame(int x, int y, boolean redraw)
4985 {
4986   int element = Feld[x][y];
4987   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4988   int last_gfx_frame = GfxFrame[x][y];
4989
4990   if (graphic_info[graphic].anim_global_sync)
4991     GfxFrame[x][y] = FrameCounter;
4992   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4993     GfxFrame[x][y] = CustomValue[x][y];
4994   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4995     GfxFrame[x][y] = element_info[element].collect_score;
4996   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4997     GfxFrame[x][y] = ChangeDelay[x][y];
4998
4999   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5000     DrawLevelGraphicAnimation(x, y, graphic);
5001 }
5002 #endif
5003
5004 static void ResetGfxAnimation(int x, int y)
5005 {
5006   GfxAction[x][y] = ACTION_DEFAULT;
5007   GfxDir[x][y] = MovDir[x][y];
5008   GfxFrame[x][y] = 0;
5009
5010 #if USE_GFX_RESET_GFX_ANIMATION
5011   ResetGfxFrame(x, y, FALSE);
5012 #endif
5013 }
5014
5015 static void ResetRandomAnimationValue(int x, int y)
5016 {
5017   GfxRandom[x][y] = INIT_GFX_RANDOM();
5018 }
5019
5020 void InitMovingField(int x, int y, int direction)
5021 {
5022   int element = Feld[x][y];
5023   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5024   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5025   int newx = x + dx;
5026   int newy = y + dy;
5027   boolean is_moving_before, is_moving_after;
5028 #if 0
5029   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5030 #endif
5031
5032   /* check if element was/is moving or being moved before/after mode change */
5033 #if 1
5034 #if 1
5035   is_moving_before = (WasJustMoving[x][y] != 0);
5036 #else
5037   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5038   is_moving_before = WasJustMoving[x][y];
5039 #endif
5040 #else
5041   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5042 #endif
5043   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5044
5045   /* reset animation only for moving elements which change direction of moving
5046      or which just started or stopped moving
5047      (else CEs with property "can move" / "not moving" are reset each frame) */
5048 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5049 #if 1
5050   if (is_moving_before != is_moving_after ||
5051       direction != MovDir[x][y])
5052     ResetGfxAnimation(x, y);
5053 #else
5054   if ((is_moving_before || is_moving_after) && !continues_moving)
5055     ResetGfxAnimation(x, y);
5056 #endif
5057 #else
5058   if (!continues_moving)
5059     ResetGfxAnimation(x, y);
5060 #endif
5061
5062   MovDir[x][y] = direction;
5063   GfxDir[x][y] = direction;
5064
5065 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5066   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5067                      direction == MV_DOWN && CAN_FALL(element) ?
5068                      ACTION_FALLING : ACTION_MOVING);
5069 #else
5070   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5071                      ACTION_FALLING : ACTION_MOVING);
5072 #endif
5073
5074   /* this is needed for CEs with property "can move" / "not moving" */
5075
5076   if (is_moving_after)
5077   {
5078     if (Feld[newx][newy] == EL_EMPTY)
5079       Feld[newx][newy] = EL_BLOCKED;
5080
5081     MovDir[newx][newy] = MovDir[x][y];
5082
5083 #if USE_NEW_CUSTOM_VALUE
5084     CustomValue[newx][newy] = CustomValue[x][y];
5085 #endif
5086
5087     GfxFrame[newx][newy] = GfxFrame[x][y];
5088     GfxRandom[newx][newy] = GfxRandom[x][y];
5089     GfxAction[newx][newy] = GfxAction[x][y];
5090     GfxDir[newx][newy] = GfxDir[x][y];
5091   }
5092 }
5093
5094 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5095 {
5096   int direction = MovDir[x][y];
5097   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5098   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5099
5100   *goes_to_x = newx;
5101   *goes_to_y = newy;
5102 }
5103
5104 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5105 {
5106   int oldx = x, oldy = y;
5107   int direction = MovDir[x][y];
5108
5109   if (direction == MV_LEFT)
5110     oldx++;
5111   else if (direction == MV_RIGHT)
5112     oldx--;
5113   else if (direction == MV_UP)
5114     oldy++;
5115   else if (direction == MV_DOWN)
5116     oldy--;
5117
5118   *comes_from_x = oldx;
5119   *comes_from_y = oldy;
5120 }
5121
5122 int MovingOrBlocked2Element(int x, int y)
5123 {
5124   int element = Feld[x][y];
5125
5126   if (element == EL_BLOCKED)
5127   {
5128     int oldx, oldy;
5129
5130     Blocked2Moving(x, y, &oldx, &oldy);
5131     return Feld[oldx][oldy];
5132   }
5133   else
5134     return element;
5135 }
5136
5137 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5138 {
5139   /* like MovingOrBlocked2Element(), but if element is moving
5140      and (x,y) is the field the moving element is just leaving,
5141      return EL_BLOCKED instead of the element value */
5142   int element = Feld[x][y];
5143
5144   if (IS_MOVING(x, y))
5145   {
5146     if (element == EL_BLOCKED)
5147     {
5148       int oldx, oldy;
5149
5150       Blocked2Moving(x, y, &oldx, &oldy);
5151       return Feld[oldx][oldy];
5152     }
5153     else
5154       return EL_BLOCKED;
5155   }
5156   else
5157     return element;
5158 }
5159
5160 static void RemoveField(int x, int y)
5161 {
5162   Feld[x][y] = EL_EMPTY;
5163
5164   MovPos[x][y] = 0;
5165   MovDir[x][y] = 0;
5166   MovDelay[x][y] = 0;
5167
5168 #if USE_NEW_CUSTOM_VALUE
5169   CustomValue[x][y] = 0;
5170 #endif
5171
5172   AmoebaNr[x][y] = 0;
5173   ChangeDelay[x][y] = 0;
5174   ChangePage[x][y] = -1;
5175   Pushed[x][y] = FALSE;
5176
5177 #if 0
5178   ExplodeField[x][y] = EX_TYPE_NONE;
5179 #endif
5180
5181   GfxElement[x][y] = EL_UNDEFINED;
5182   GfxAction[x][y] = ACTION_DEFAULT;
5183   GfxDir[x][y] = MV_NONE;
5184 #if 0
5185   /* !!! this would prevent the removed tile from being redrawn !!! */
5186   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5187 #endif
5188 }
5189
5190 void RemoveMovingField(int x, int y)
5191 {
5192   int oldx = x, oldy = y, newx = x, newy = y;
5193   int element = Feld[x][y];
5194   int next_element = EL_UNDEFINED;
5195
5196   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5197     return;
5198
5199   if (IS_MOVING(x, y))
5200   {
5201     Moving2Blocked(x, y, &newx, &newy);
5202
5203     if (Feld[newx][newy] != EL_BLOCKED)
5204     {
5205       /* element is moving, but target field is not free (blocked), but
5206          already occupied by something different (example: acid pool);
5207          in this case, only remove the moving field, but not the target */
5208
5209       RemoveField(oldx, oldy);
5210
5211       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5212
5213       TEST_DrawLevelField(oldx, oldy);
5214
5215       return;
5216     }
5217   }
5218   else if (element == EL_BLOCKED)
5219   {
5220     Blocked2Moving(x, y, &oldx, &oldy);
5221     if (!IS_MOVING(oldx, oldy))
5222       return;
5223   }
5224
5225   if (element == EL_BLOCKED &&
5226       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5227        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5228        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5229        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5230        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5231        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5232     next_element = get_next_element(Feld[oldx][oldy]);
5233
5234   RemoveField(oldx, oldy);
5235   RemoveField(newx, newy);
5236
5237   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5238
5239   if (next_element != EL_UNDEFINED)
5240     Feld[oldx][oldy] = next_element;
5241
5242   TEST_DrawLevelField(oldx, oldy);
5243   TEST_DrawLevelField(newx, newy);
5244 }
5245
5246 void DrawDynamite(int x, int y)
5247 {
5248   int sx = SCREENX(x), sy = SCREENY(y);
5249   int graphic = el2img(Feld[x][y]);
5250   int frame;
5251
5252   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5253     return;
5254
5255   if (IS_WALKABLE_INSIDE(Back[x][y]))
5256     return;
5257
5258   if (Back[x][y])
5259     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5260   else if (Store[x][y])
5261     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5262
5263   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5264
5265   if (Back[x][y] || Store[x][y])
5266     DrawGraphicThruMask(sx, sy, graphic, frame);
5267   else
5268     DrawGraphic(sx, sy, graphic, frame);
5269 }
5270
5271 void CheckDynamite(int x, int y)
5272 {
5273   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5274   {
5275     MovDelay[x][y]--;
5276
5277     if (MovDelay[x][y] != 0)
5278     {
5279       DrawDynamite(x, y);
5280       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5281
5282       return;
5283     }
5284   }
5285
5286   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5287
5288   Bang(x, y);
5289 }
5290
5291 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5292 {
5293   boolean num_checked_players = 0;
5294   int i;
5295
5296   for (i = 0; i < MAX_PLAYERS; i++)
5297   {
5298     if (stored_player[i].active)
5299     {
5300       int sx = stored_player[i].jx;
5301       int sy = stored_player[i].jy;
5302
5303       if (num_checked_players == 0)
5304       {
5305         *sx1 = *sx2 = sx;
5306         *sy1 = *sy2 = sy;
5307       }
5308       else
5309       {
5310         *sx1 = MIN(*sx1, sx);
5311         *sy1 = MIN(*sy1, sy);
5312         *sx2 = MAX(*sx2, sx);
5313         *sy2 = MAX(*sy2, sy);
5314       }
5315
5316       num_checked_players++;
5317     }
5318   }
5319 }
5320
5321 static boolean checkIfAllPlayersFitToScreen_RND()
5322 {
5323   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5324
5325   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5326
5327   return (sx2 - sx1 < SCR_FIELDX &&
5328           sy2 - sy1 < SCR_FIELDY);
5329 }
5330
5331 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5332 {
5333   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5334
5335   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5336
5337   *sx = (sx1 + sx2) / 2;
5338   *sy = (sy1 + sy2) / 2;
5339 }
5340
5341 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5342                         boolean center_screen, boolean quick_relocation)
5343 {
5344   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5345   boolean no_delay = (tape.warp_forward);
5346   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5347   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5348
5349   if (quick_relocation)
5350   {
5351     int offset = game.scroll_delay_value;
5352
5353     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5354     {
5355       if (!level.shifted_relocation || center_screen)
5356       {
5357         /* quick relocation (without scrolling), with centering of screen */
5358
5359         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5360                     x > SBX_Right + MIDPOSX ? SBX_Right :
5361                     x - MIDPOSX);
5362
5363         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5364                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5365                     y - MIDPOSY);
5366       }
5367       else
5368       {
5369         /* quick relocation (without scrolling), but do not center screen */
5370
5371         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5372                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5373                                old_x - MIDPOSX);
5374
5375         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5376                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5377                                old_y - MIDPOSY);
5378
5379         int offset_x = x + (scroll_x - center_scroll_x);
5380         int offset_y = y + (scroll_y - center_scroll_y);
5381
5382         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5383                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5384                     offset_x - MIDPOSX);
5385
5386         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5387                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5388                     offset_y - MIDPOSY);
5389       }
5390     }
5391     else
5392     {
5393       /* quick relocation (without scrolling), inside visible screen area */
5394
5395       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5396           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5397         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5398
5399       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5400           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5401         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5402
5403       /* don't scroll over playfield boundaries */
5404       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5405         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5406
5407       /* don't scroll over playfield boundaries */
5408       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5409         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5410     }
5411
5412     RedrawPlayfield(TRUE, 0,0,0,0);
5413   }
5414   else
5415   {
5416 #if 1
5417     int scroll_xx, scroll_yy;
5418
5419     if (!level.shifted_relocation || center_screen)
5420     {
5421       /* visible relocation (with scrolling), with centering of screen */
5422
5423       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5424                    x > SBX_Right + MIDPOSX ? SBX_Right :
5425                    x - MIDPOSX);
5426
5427       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5428                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5429                    y - MIDPOSY);
5430     }
5431     else
5432     {
5433       /* visible relocation (with scrolling), but do not center screen */
5434
5435       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5436                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5437                              old_x - MIDPOSX);
5438
5439       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441                              old_y - MIDPOSY);
5442
5443       int offset_x = x + (scroll_x - center_scroll_x);
5444       int offset_y = y + (scroll_y - center_scroll_y);
5445
5446       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5447                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5448                    offset_x - MIDPOSX);
5449
5450       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5451                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5452                    offset_y - MIDPOSY);
5453     }
5454
5455 #else
5456
5457     /* visible relocation (with scrolling), with centering of screen */
5458
5459     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5460                      x > SBX_Right + MIDPOSX ? SBX_Right :
5461                      x - MIDPOSX);
5462
5463     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5464                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5465                      y - MIDPOSY);
5466 #endif
5467
5468     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5469
5470     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5471     {
5472       int dx = 0, dy = 0;
5473       int fx = FX, fy = FY;
5474
5475       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5476       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5477
5478       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5479         break;
5480
5481       scroll_x -= dx;
5482       scroll_y -= dy;
5483
5484       fx += dx * TILEX / 2;
5485       fy += dy * TILEY / 2;
5486
5487       ScrollLevel(dx, dy);
5488       DrawAllPlayers();
5489
5490       /* scroll in two steps of half tile size to make things smoother */
5491       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5492       FlushDisplay();
5493       Delay(wait_delay_value);
5494
5495       /* scroll second step to align at full tile size */
5496       BackToFront();
5497       Delay(wait_delay_value);
5498     }
5499
5500     DrawAllPlayers();
5501     BackToFront();
5502     Delay(wait_delay_value);
5503   }
5504 }
5505
5506 void RelocatePlayer(int jx, int jy, int el_player_raw)
5507 {
5508   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5509   int player_nr = GET_PLAYER_NR(el_player);
5510   struct PlayerInfo *player = &stored_player[player_nr];
5511   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5512   boolean no_delay = (tape.warp_forward);
5513   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5514   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5515   int old_jx = player->jx;
5516   int old_jy = player->jy;
5517   int old_element = Feld[old_jx][old_jy];
5518   int element = Feld[jx][jy];
5519   boolean player_relocated = (old_jx != jx || old_jy != jy);
5520
5521   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5522   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5523   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5524   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5525   int leave_side_horiz = move_dir_horiz;
5526   int leave_side_vert  = move_dir_vert;
5527   int enter_side = enter_side_horiz | enter_side_vert;
5528   int leave_side = leave_side_horiz | leave_side_vert;
5529
5530   if (player->GameOver)         /* do not reanimate dead player */
5531     return;
5532
5533   if (!player_relocated)        /* no need to relocate the player */
5534     return;
5535
5536   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5537   {
5538     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5539     DrawLevelField(jx, jy);
5540   }
5541
5542   if (player->present)
5543   {
5544     while (player->MovPos)
5545     {
5546       ScrollPlayer(player, SCROLL_GO_ON);
5547       ScrollScreen(NULL, SCROLL_GO_ON);
5548
5549       AdvanceFrameAndPlayerCounters(player->index_nr);
5550
5551       DrawPlayer(player);
5552
5553       BackToFront();
5554       Delay(wait_delay_value);
5555     }
5556
5557     DrawPlayer(player);         /* needed here only to cleanup last field */
5558     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5559
5560     player->is_moving = FALSE;
5561   }
5562
5563   if (IS_CUSTOM_ELEMENT(old_element))
5564     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5565                                CE_LEFT_BY_PLAYER,
5566                                player->index_bit, leave_side);
5567
5568   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5569                                       CE_PLAYER_LEAVES_X,
5570                                       player->index_bit, leave_side);
5571
5572   Feld[jx][jy] = el_player;
5573   InitPlayerField(jx, jy, el_player, TRUE);
5574
5575   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5576   {
5577     Feld[jx][jy] = element;
5578     InitField(jx, jy, FALSE);
5579   }
5580
5581   /* only visually relocate centered player */
5582   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5583                      FALSE, level.instant_relocation);
5584
5585   TestIfPlayerTouchesBadThing(jx, jy);
5586   TestIfPlayerTouchesCustomElement(jx, jy);
5587
5588   if (IS_CUSTOM_ELEMENT(element))
5589     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5590                                player->index_bit, enter_side);
5591
5592   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5593                                       player->index_bit, enter_side);
5594 }
5595
5596 void Explode(int ex, int ey, int phase, int mode)
5597 {
5598   int x, y;
5599   int last_phase;
5600   int border_element;
5601
5602   /* !!! eliminate this variable !!! */
5603   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5604
5605   if (game.explosions_delayed)
5606   {
5607     ExplodeField[ex][ey] = mode;
5608     return;
5609   }
5610
5611   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5612   {
5613     int center_element = Feld[ex][ey];
5614     int artwork_element, explosion_element;     /* set these values later */
5615
5616 #if 0
5617     /* --- This is only really needed (and now handled) in "Impact()". --- */
5618     /* do not explode moving elements that left the explode field in time */
5619     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5620         center_element == EL_EMPTY &&
5621         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5622       return;
5623 #endif
5624
5625 #if 0
5626     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5627     if (mode == EX_TYPE_NORMAL ||
5628         mode == EX_TYPE_CENTER ||
5629         mode == EX_TYPE_CROSS)
5630       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5631 #endif
5632
5633     /* remove things displayed in background while burning dynamite */
5634     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5635       Back[ex][ey] = 0;
5636
5637     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5638     {
5639       /* put moving element to center field (and let it explode there) */
5640       center_element = MovingOrBlocked2Element(ex, ey);
5641       RemoveMovingField(ex, ey);
5642       Feld[ex][ey] = center_element;
5643     }
5644
5645     /* now "center_element" is finally determined -- set related values now */
5646     artwork_element = center_element;           /* for custom player artwork */
5647     explosion_element = center_element;         /* for custom player artwork */
5648
5649     if (IS_PLAYER(ex, ey))
5650     {
5651       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5652
5653       artwork_element = stored_player[player_nr].artwork_element;
5654
5655       if (level.use_explosion_element[player_nr])
5656       {
5657         explosion_element = level.explosion_element[player_nr];
5658         artwork_element = explosion_element;
5659       }
5660     }
5661
5662 #if 1
5663     if (mode == EX_TYPE_NORMAL ||
5664         mode == EX_TYPE_CENTER ||
5665         mode == EX_TYPE_CROSS)
5666       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5667 #endif
5668
5669     last_phase = element_info[explosion_element].explosion_delay + 1;
5670
5671     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5672     {
5673       int xx = x - ex + 1;
5674       int yy = y - ey + 1;
5675       int element;
5676
5677       if (!IN_LEV_FIELD(x, y) ||
5678           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5679           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5680         continue;
5681
5682       element = Feld[x][y];
5683
5684       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5685       {
5686         element = MovingOrBlocked2Element(x, y);
5687
5688         if (!IS_EXPLOSION_PROOF(element))
5689           RemoveMovingField(x, y);
5690       }
5691
5692       /* indestructible elements can only explode in center (but not flames) */
5693       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5694                                            mode == EX_TYPE_BORDER)) ||
5695           element == EL_FLAMES)
5696         continue;
5697
5698       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5699          behaviour, for example when touching a yamyam that explodes to rocks
5700          with active deadly shield, a rock is created under the player !!! */
5701       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5702 #if 0
5703       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5704           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5705            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5706 #else
5707       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5708 #endif
5709       {
5710         if (IS_ACTIVE_BOMB(element))
5711         {
5712           /* re-activate things under the bomb like gate or penguin */
5713           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5714           Back[x][y] = 0;
5715         }
5716
5717         continue;
5718       }
5719
5720       /* save walkable background elements while explosion on same tile */
5721       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5722           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5723         Back[x][y] = element;
5724
5725       /* ignite explodable elements reached by other explosion */
5726       if (element == EL_EXPLOSION)
5727         element = Store2[x][y];
5728
5729       if (AmoebaNr[x][y] &&
5730           (element == EL_AMOEBA_FULL ||
5731            element == EL_BD_AMOEBA ||
5732            element == EL_AMOEBA_GROWING))
5733       {
5734         AmoebaCnt[AmoebaNr[x][y]]--;
5735         AmoebaCnt2[AmoebaNr[x][y]]--;
5736       }
5737
5738       RemoveField(x, y);
5739
5740       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5741       {
5742         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5743
5744         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5745
5746         if (PLAYERINFO(ex, ey)->use_murphy)
5747           Store[x][y] = EL_EMPTY;
5748       }
5749
5750       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5751          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5752       else if (ELEM_IS_PLAYER(center_element))
5753         Store[x][y] = EL_EMPTY;
5754       else if (center_element == EL_YAMYAM)
5755         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5756       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5757         Store[x][y] = element_info[center_element].content.e[xx][yy];
5758 #if 1
5759       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5760          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5761          otherwise) -- FIX THIS !!! */
5762       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5763         Store[x][y] = element_info[element].content.e[1][1];
5764 #else
5765       else if (!CAN_EXPLODE(element))
5766         Store[x][y] = element_info[element].content.e[1][1];
5767 #endif
5768       else
5769         Store[x][y] = EL_EMPTY;
5770
5771       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5772           center_element == EL_AMOEBA_TO_DIAMOND)
5773         Store2[x][y] = element;
5774
5775       Feld[x][y] = EL_EXPLOSION;
5776       GfxElement[x][y] = artwork_element;
5777
5778       ExplodePhase[x][y] = 1;
5779       ExplodeDelay[x][y] = last_phase;
5780
5781       Stop[x][y] = TRUE;
5782     }
5783
5784     if (center_element == EL_YAMYAM)
5785       game.yamyam_content_nr =
5786         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5787
5788     return;
5789   }
5790
5791   if (Stop[ex][ey])
5792     return;
5793
5794   x = ex;
5795   y = ey;
5796
5797   if (phase == 1)
5798     GfxFrame[x][y] = 0;         /* restart explosion animation */
5799
5800   last_phase = ExplodeDelay[x][y];
5801
5802   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5803
5804 #ifdef DEBUG
5805
5806   /* activate this even in non-DEBUG version until cause for crash in
5807      getGraphicAnimationFrame() (see below) is found and eliminated */
5808
5809 #endif
5810 #if 1
5811
5812 #if 1
5813   /* this can happen if the player leaves an explosion just in time */
5814   if (GfxElement[x][y] == EL_UNDEFINED)
5815     GfxElement[x][y] = EL_EMPTY;
5816 #else
5817   if (GfxElement[x][y] == EL_UNDEFINED)
5818   {
5819     printf("\n\n");
5820     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5821     printf("Explode(): This should never happen!\n");
5822     printf("\n\n");
5823
5824     GfxElement[x][y] = EL_EMPTY;
5825   }
5826 #endif
5827
5828 #endif
5829
5830   border_element = Store2[x][y];
5831   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5832     border_element = StorePlayer[x][y];
5833
5834   if (phase == element_info[border_element].ignition_delay ||
5835       phase == last_phase)
5836   {
5837     boolean border_explosion = FALSE;
5838
5839     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5840         !PLAYER_EXPLOSION_PROTECTED(x, y))
5841     {
5842       KillPlayerUnlessExplosionProtected(x, y);
5843       border_explosion = TRUE;
5844     }
5845     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5846     {
5847       Feld[x][y] = Store2[x][y];
5848       Store2[x][y] = 0;
5849       Bang(x, y);
5850       border_explosion = TRUE;
5851     }
5852     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5853     {
5854       AmoebeUmwandeln(x, y);
5855       Store2[x][y] = 0;
5856       border_explosion = TRUE;
5857     }
5858
5859     /* if an element just explodes due to another explosion (chain-reaction),
5860        do not immediately end the new explosion when it was the last frame of
5861        the explosion (as it would be done in the following "if"-statement!) */
5862     if (border_explosion && phase == last_phase)
5863       return;
5864   }
5865
5866   if (phase == last_phase)
5867   {
5868     int element;
5869
5870     element = Feld[x][y] = Store[x][y];
5871     Store[x][y] = Store2[x][y] = 0;
5872     GfxElement[x][y] = EL_UNDEFINED;
5873
5874     /* player can escape from explosions and might therefore be still alive */
5875     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5876         element <= EL_PLAYER_IS_EXPLODING_4)
5877     {
5878       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5879       int explosion_element = EL_PLAYER_1 + player_nr;
5880       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5881       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5882
5883       if (level.use_explosion_element[player_nr])
5884         explosion_element = level.explosion_element[player_nr];
5885
5886       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5887                     element_info[explosion_element].content.e[xx][yy]);
5888     }
5889
5890     /* restore probably existing indestructible background element */
5891     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5892       element = Feld[x][y] = Back[x][y];
5893     Back[x][y] = 0;
5894
5895     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5896     GfxDir[x][y] = MV_NONE;
5897     ChangeDelay[x][y] = 0;
5898     ChangePage[x][y] = -1;
5899
5900 #if USE_NEW_CUSTOM_VALUE
5901     CustomValue[x][y] = 0;
5902 #endif
5903
5904     InitField_WithBug2(x, y, FALSE);
5905
5906     TEST_DrawLevelField(x, y);
5907
5908     TestIfElementTouchesCustomElement(x, y);
5909
5910     if (GFX_CRUMBLED(element))
5911       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5912
5913     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5914       StorePlayer[x][y] = 0;
5915
5916     if (ELEM_IS_PLAYER(element))
5917       RelocatePlayer(x, y, element);
5918   }
5919   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5920   {
5921     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5922     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5923
5924     if (phase == delay)
5925       TEST_DrawLevelFieldCrumbledSand(x, y);
5926
5927     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5928     {
5929       DrawLevelElement(x, y, Back[x][y]);
5930       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5931     }
5932     else if (IS_WALKABLE_UNDER(Back[x][y]))
5933     {
5934       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5935       DrawLevelElementThruMask(x, y, Back[x][y]);
5936     }
5937     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5938       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5939   }
5940 }
5941
5942 void DynaExplode(int ex, int ey)
5943 {
5944   int i, j;
5945   int dynabomb_element = Feld[ex][ey];
5946   int dynabomb_size = 1;
5947   boolean dynabomb_xl = FALSE;
5948   struct PlayerInfo *player;
5949   static int xy[4][2] =
5950   {
5951     { 0, -1 },
5952     { -1, 0 },
5953     { +1, 0 },
5954     { 0, +1 }
5955   };
5956
5957   if (IS_ACTIVE_BOMB(dynabomb_element))
5958   {
5959     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5960     dynabomb_size = player->dynabomb_size;
5961     dynabomb_xl = player->dynabomb_xl;
5962     player->dynabombs_left++;
5963   }
5964
5965   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5966
5967   for (i = 0; i < NUM_DIRECTIONS; i++)
5968   {
5969     for (j = 1; j <= dynabomb_size; j++)
5970     {
5971       int x = ex + j * xy[i][0];
5972       int y = ey + j * xy[i][1];
5973       int element;
5974
5975       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5976         break;
5977
5978       element = Feld[x][y];
5979
5980       /* do not restart explosions of fields with active bombs */
5981       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5982         continue;
5983
5984       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5985
5986       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5987           !IS_DIGGABLE(element) && !dynabomb_xl)
5988         break;
5989     }
5990   }
5991 }
5992
5993 void Bang(int x, int y)
5994 {
5995   int element = MovingOrBlocked2Element(x, y);
5996   int explosion_type = EX_TYPE_NORMAL;
5997
5998   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5999   {
6000     struct PlayerInfo *player = PLAYERINFO(x, y);
6001
6002 #if USE_FIX_CE_ACTION_WITH_PLAYER
6003     element = Feld[x][y] = player->initial_element;
6004 #else
6005     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6006                             player->element_nr);
6007 #endif
6008
6009     if (level.use_explosion_element[player->index_nr])
6010     {
6011       int explosion_element = level.explosion_element[player->index_nr];
6012
6013       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6014         explosion_type = EX_TYPE_CROSS;
6015       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6016         explosion_type = EX_TYPE_CENTER;
6017     }
6018   }
6019
6020   switch (element)
6021   {
6022     case EL_BUG:
6023     case EL_SPACESHIP:
6024     case EL_BD_BUTTERFLY:
6025     case EL_BD_FIREFLY:
6026     case EL_YAMYAM:
6027     case EL_DARK_YAMYAM:
6028     case EL_ROBOT:
6029     case EL_PACMAN:
6030     case EL_MOLE:
6031       RaiseScoreElement(element);
6032       break;
6033
6034     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6035     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6036     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6037     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6038     case EL_DYNABOMB_INCREASE_NUMBER:
6039     case EL_DYNABOMB_INCREASE_SIZE:
6040     case EL_DYNABOMB_INCREASE_POWER:
6041       explosion_type = EX_TYPE_DYNA;
6042       break;
6043
6044     case EL_DC_LANDMINE:
6045 #if 0
6046     case EL_EM_EXIT_OPEN:
6047     case EL_EM_STEEL_EXIT_OPEN:
6048 #endif
6049       explosion_type = EX_TYPE_CENTER;
6050       break;
6051
6052     case EL_PENGUIN:
6053     case EL_LAMP:
6054     case EL_LAMP_ACTIVE:
6055     case EL_AMOEBA_TO_DIAMOND:
6056       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6057         explosion_type = EX_TYPE_CENTER;
6058       break;
6059
6060     default:
6061       if (element_info[element].explosion_type == EXPLODES_CROSS)
6062         explosion_type = EX_TYPE_CROSS;
6063       else if (element_info[element].explosion_type == EXPLODES_1X1)
6064         explosion_type = EX_TYPE_CENTER;
6065       break;
6066   }
6067
6068   if (explosion_type == EX_TYPE_DYNA)
6069     DynaExplode(x, y);
6070   else
6071     Explode(x, y, EX_PHASE_START, explosion_type);
6072
6073   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6074 }
6075
6076 void SplashAcid(int x, int y)
6077 {
6078   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6079       (!IN_LEV_FIELD(x - 1, y - 2) ||
6080        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6081     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6082
6083   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6084       (!IN_LEV_FIELD(x + 1, y - 2) ||
6085        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6086     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6087
6088   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6089 }
6090
6091 static void InitBeltMovement()
6092 {
6093   static int belt_base_element[4] =
6094   {
6095     EL_CONVEYOR_BELT_1_LEFT,
6096     EL_CONVEYOR_BELT_2_LEFT,
6097     EL_CONVEYOR_BELT_3_LEFT,
6098     EL_CONVEYOR_BELT_4_LEFT
6099   };
6100   static int belt_base_active_element[4] =
6101   {
6102     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6103     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6104     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6105     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6106   };
6107
6108   int x, y, i, j;
6109
6110   /* set frame order for belt animation graphic according to belt direction */
6111   for (i = 0; i < NUM_BELTS; i++)
6112   {
6113     int belt_nr = i;
6114
6115     for (j = 0; j < NUM_BELT_PARTS; j++)
6116     {
6117       int element = belt_base_active_element[belt_nr] + j;
6118       int graphic_1 = el2img(element);
6119       int graphic_2 = el2panelimg(element);
6120
6121       if (game.belt_dir[i] == MV_LEFT)
6122       {
6123         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6124         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6125       }
6126       else
6127       {
6128         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6129         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6130       }
6131     }
6132   }
6133
6134   SCAN_PLAYFIELD(x, y)
6135   {
6136     int element = Feld[x][y];
6137
6138     for (i = 0; i < NUM_BELTS; i++)
6139     {
6140       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6141       {
6142         int e_belt_nr = getBeltNrFromBeltElement(element);
6143         int belt_nr = i;
6144
6145         if (e_belt_nr == belt_nr)
6146         {
6147           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6148
6149           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6150         }
6151       }
6152     }
6153   }
6154 }
6155
6156 static void ToggleBeltSwitch(int x, int y)
6157 {
6158   static int belt_base_element[4] =
6159   {
6160     EL_CONVEYOR_BELT_1_LEFT,
6161     EL_CONVEYOR_BELT_2_LEFT,
6162     EL_CONVEYOR_BELT_3_LEFT,
6163     EL_CONVEYOR_BELT_4_LEFT
6164   };
6165   static int belt_base_active_element[4] =
6166   {
6167     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6170     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6171   };
6172   static int belt_base_switch_element[4] =
6173   {
6174     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6175     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6176     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6177     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6178   };
6179   static int belt_move_dir[4] =
6180   {
6181     MV_LEFT,
6182     MV_NONE,
6183     MV_RIGHT,
6184     MV_NONE,
6185   };
6186
6187   int element = Feld[x][y];
6188   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6189   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6190   int belt_dir = belt_move_dir[belt_dir_nr];
6191   int xx, yy, i;
6192
6193   if (!IS_BELT_SWITCH(element))
6194     return;
6195
6196   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6197   game.belt_dir[belt_nr] = belt_dir;
6198
6199   if (belt_dir_nr == 3)
6200     belt_dir_nr = 1;
6201
6202   /* set frame order for belt animation graphic according to belt direction */
6203   for (i = 0; i < NUM_BELT_PARTS; i++)
6204   {
6205     int element = belt_base_active_element[belt_nr] + i;
6206     int graphic_1 = el2img(element);
6207     int graphic_2 = el2panelimg(element);
6208
6209     if (belt_dir == MV_LEFT)
6210     {
6211       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6212       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6213     }
6214     else
6215     {
6216       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6217       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6218     }
6219   }
6220
6221   SCAN_PLAYFIELD(xx, yy)
6222   {
6223     int element = Feld[xx][yy];
6224
6225     if (IS_BELT_SWITCH(element))
6226     {
6227       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6228
6229       if (e_belt_nr == belt_nr)
6230       {
6231         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6232         TEST_DrawLevelField(xx, yy);
6233       }
6234     }
6235     else if (IS_BELT(element) && belt_dir != MV_NONE)
6236     {
6237       int e_belt_nr = getBeltNrFromBeltElement(element);
6238
6239       if (e_belt_nr == belt_nr)
6240       {
6241         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6242
6243         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6244         TEST_DrawLevelField(xx, yy);
6245       }
6246     }
6247     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6248     {
6249       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6250
6251       if (e_belt_nr == belt_nr)
6252       {
6253         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6254
6255         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6256         TEST_DrawLevelField(xx, yy);
6257       }
6258     }
6259   }
6260 }
6261
6262 static void ToggleSwitchgateSwitch(int x, int y)
6263 {
6264   int xx, yy;
6265
6266   game.switchgate_pos = !game.switchgate_pos;
6267
6268   SCAN_PLAYFIELD(xx, yy)
6269   {
6270     int element = Feld[xx][yy];
6271
6272 #if !USE_BOTH_SWITCHGATE_SWITCHES
6273     if (element == EL_SWITCHGATE_SWITCH_UP ||
6274         element == EL_SWITCHGATE_SWITCH_DOWN)
6275     {
6276       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6277       TEST_DrawLevelField(xx, yy);
6278     }
6279     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6280              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6281     {
6282       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6283       TEST_DrawLevelField(xx, yy);
6284     }
6285 #else
6286     if (element == EL_SWITCHGATE_SWITCH_UP)
6287     {
6288       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6289       TEST_DrawLevelField(xx, yy);
6290     }
6291     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6292     {
6293       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6294       TEST_DrawLevelField(xx, yy);
6295     }
6296     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6297     {
6298       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6299       TEST_DrawLevelField(xx, yy);
6300     }
6301     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6302     {
6303       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6304       TEST_DrawLevelField(xx, yy);
6305     }
6306 #endif
6307     else if (element == EL_SWITCHGATE_OPEN ||
6308              element == EL_SWITCHGATE_OPENING)
6309     {
6310       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6311
6312       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6313     }
6314     else if (element == EL_SWITCHGATE_CLOSED ||
6315              element == EL_SWITCHGATE_CLOSING)
6316     {
6317       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6318
6319       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6320     }
6321   }
6322 }
6323
6324 static int getInvisibleActiveFromInvisibleElement(int element)
6325 {
6326   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6327           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6328           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6329           element);
6330 }
6331
6332 static int getInvisibleFromInvisibleActiveElement(int element)
6333 {
6334   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6335           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6336           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6337           element);
6338 }
6339
6340 static void RedrawAllLightSwitchesAndInvisibleElements()
6341 {
6342   int x, y;
6343
6344   SCAN_PLAYFIELD(x, y)
6345   {
6346     int element = Feld[x][y];
6347
6348     if (element == EL_LIGHT_SWITCH &&
6349         game.light_time_left > 0)
6350     {
6351       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6352       TEST_DrawLevelField(x, y);
6353     }
6354     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6355              game.light_time_left == 0)
6356     {
6357       Feld[x][y] = EL_LIGHT_SWITCH;
6358       TEST_DrawLevelField(x, y);
6359     }
6360     else if (element == EL_EMC_DRIPPER &&
6361              game.light_time_left > 0)
6362     {
6363       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6364       TEST_DrawLevelField(x, y);
6365     }
6366     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6367              game.light_time_left == 0)
6368     {
6369       Feld[x][y] = EL_EMC_DRIPPER;
6370       TEST_DrawLevelField(x, y);
6371     }
6372     else if (element == EL_INVISIBLE_STEELWALL ||
6373              element == EL_INVISIBLE_WALL ||
6374              element == EL_INVISIBLE_SAND)
6375     {
6376       if (game.light_time_left > 0)
6377         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6378
6379       TEST_DrawLevelField(x, y);
6380
6381       /* uncrumble neighbour fields, if needed */
6382       if (element == EL_INVISIBLE_SAND)
6383         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6384     }
6385     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6386              element == EL_INVISIBLE_WALL_ACTIVE ||
6387              element == EL_INVISIBLE_SAND_ACTIVE)
6388     {
6389       if (game.light_time_left == 0)
6390         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6391
6392       TEST_DrawLevelField(x, y);
6393
6394       /* re-crumble neighbour fields, if needed */
6395       if (element == EL_INVISIBLE_SAND)
6396         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6397     }
6398   }
6399 }
6400
6401 static void RedrawAllInvisibleElementsForLenses()
6402 {
6403   int x, y;
6404
6405   SCAN_PLAYFIELD(x, y)
6406   {
6407     int element = Feld[x][y];
6408
6409     if (element == EL_EMC_DRIPPER &&
6410         game.lenses_time_left > 0)
6411     {
6412       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6413       TEST_DrawLevelField(x, y);
6414     }
6415     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6416              game.lenses_time_left == 0)
6417     {
6418       Feld[x][y] = EL_EMC_DRIPPER;
6419       TEST_DrawLevelField(x, y);
6420     }
6421     else if (element == EL_INVISIBLE_STEELWALL ||
6422              element == EL_INVISIBLE_WALL ||
6423              element == EL_INVISIBLE_SAND)
6424     {
6425       if (game.lenses_time_left > 0)
6426         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6427
6428       TEST_DrawLevelField(x, y);
6429
6430       /* uncrumble neighbour fields, if needed */
6431       if (element == EL_INVISIBLE_SAND)
6432         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6433     }
6434     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6435              element == EL_INVISIBLE_WALL_ACTIVE ||
6436              element == EL_INVISIBLE_SAND_ACTIVE)
6437     {
6438       if (game.lenses_time_left == 0)
6439         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6440
6441       TEST_DrawLevelField(x, y);
6442
6443       /* re-crumble neighbour fields, if needed */
6444       if (element == EL_INVISIBLE_SAND)
6445         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6446     }
6447   }
6448 }
6449
6450 static void RedrawAllInvisibleElementsForMagnifier()
6451 {
6452   int x, y;
6453
6454   SCAN_PLAYFIELD(x, y)
6455   {
6456     int element = Feld[x][y];
6457
6458     if (element == EL_EMC_FAKE_GRASS &&
6459         game.magnify_time_left > 0)
6460     {
6461       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6462       TEST_DrawLevelField(x, y);
6463     }
6464     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6465              game.magnify_time_left == 0)
6466     {
6467       Feld[x][y] = EL_EMC_FAKE_GRASS;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (IS_GATE_GRAY(element) &&
6471              game.magnify_time_left > 0)
6472     {
6473       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6474                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6475                     IS_EM_GATE_GRAY(element) ?
6476                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6477                     IS_EMC_GATE_GRAY(element) ?
6478                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6479                     IS_DC_GATE_GRAY(element) ?
6480                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6481                     element);
6482       TEST_DrawLevelField(x, y);
6483     }
6484     else if (IS_GATE_GRAY_ACTIVE(element) &&
6485              game.magnify_time_left == 0)
6486     {
6487       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6488                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6489                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6490                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6491                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6492                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6493                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6494                     EL_DC_GATE_WHITE_GRAY :
6495                     element);
6496       TEST_DrawLevelField(x, y);
6497     }
6498   }
6499 }
6500
6501 static void ToggleLightSwitch(int x, int y)
6502 {
6503   int element = Feld[x][y];
6504
6505   game.light_time_left =
6506     (element == EL_LIGHT_SWITCH ?
6507      level.time_light * FRAMES_PER_SECOND : 0);
6508
6509   RedrawAllLightSwitchesAndInvisibleElements();
6510 }
6511
6512 static void ActivateTimegateSwitch(int x, int y)
6513 {
6514   int xx, yy;
6515
6516   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6517
6518   SCAN_PLAYFIELD(xx, yy)
6519   {
6520     int element = Feld[xx][yy];
6521
6522     if (element == EL_TIMEGATE_CLOSED ||
6523         element == EL_TIMEGATE_CLOSING)
6524     {
6525       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6526       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6527     }
6528
6529     /*
6530     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6531     {
6532       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535     */
6536
6537   }
6538
6539 #if 1
6540   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6541                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6542 #else
6543   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6544 #endif
6545 }
6546
6547 void Impact(int x, int y)
6548 {
6549   boolean last_line = (y == lev_fieldy - 1);
6550   boolean object_hit = FALSE;
6551   boolean impact = (last_line || object_hit);
6552   int element = Feld[x][y];
6553   int smashed = EL_STEELWALL;
6554
6555   if (!last_line)       /* check if element below was hit */
6556   {
6557     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6558       return;
6559
6560     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6561                                          MovDir[x][y + 1] != MV_DOWN ||
6562                                          MovPos[x][y + 1] <= TILEY / 2));
6563
6564     /* do not smash moving elements that left the smashed field in time */
6565     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6566         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6567       object_hit = FALSE;
6568
6569 #if USE_QUICKSAND_IMPACT_BUGFIX
6570     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6571     {
6572       RemoveMovingField(x, y + 1);
6573       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6574       Feld[x][y + 2] = EL_ROCK;
6575       TEST_DrawLevelField(x, y + 2);
6576
6577       object_hit = TRUE;
6578     }
6579
6580     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6581     {
6582       RemoveMovingField(x, y + 1);
6583       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6584       Feld[x][y + 2] = EL_ROCK;
6585       TEST_DrawLevelField(x, y + 2);
6586
6587       object_hit = TRUE;
6588     }
6589 #endif
6590
6591     if (object_hit)
6592       smashed = MovingOrBlocked2Element(x, y + 1);
6593
6594     impact = (last_line || object_hit);
6595   }
6596
6597   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6598   {
6599     SplashAcid(x, y + 1);
6600     return;
6601   }
6602
6603   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6604   /* only reset graphic animation if graphic really changes after impact */
6605   if (impact &&
6606       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6607   {
6608     ResetGfxAnimation(x, y);
6609     TEST_DrawLevelField(x, y);
6610   }
6611
6612   if (impact && CAN_EXPLODE_IMPACT(element))
6613   {
6614     Bang(x, y);
6615     return;
6616   }
6617   else if (impact && element == EL_PEARL &&
6618            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6619   {
6620     ResetGfxAnimation(x, y);
6621
6622     Feld[x][y] = EL_PEARL_BREAKING;
6623     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6624     return;
6625   }
6626   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6627   {
6628     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6629
6630     return;
6631   }
6632
6633   if (impact && element == EL_AMOEBA_DROP)
6634   {
6635     if (object_hit && IS_PLAYER(x, y + 1))
6636       KillPlayerUnlessEnemyProtected(x, y + 1);
6637     else if (object_hit && smashed == EL_PENGUIN)
6638       Bang(x, y + 1);
6639     else
6640     {
6641       Feld[x][y] = EL_AMOEBA_GROWING;
6642       Store[x][y] = EL_AMOEBA_WET;
6643
6644       ResetRandomAnimationValue(x, y);
6645     }
6646     return;
6647   }
6648
6649   if (object_hit)               /* check which object was hit */
6650   {
6651     if ((CAN_PASS_MAGIC_WALL(element) && 
6652          (smashed == EL_MAGIC_WALL ||
6653           smashed == EL_BD_MAGIC_WALL)) ||
6654         (CAN_PASS_DC_MAGIC_WALL(element) &&
6655          smashed == EL_DC_MAGIC_WALL))
6656     {
6657       int xx, yy;
6658       int activated_magic_wall =
6659         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6660          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6661          EL_DC_MAGIC_WALL_ACTIVE);
6662
6663       /* activate magic wall / mill */
6664       SCAN_PLAYFIELD(xx, yy)
6665       {
6666         if (Feld[xx][yy] == smashed)
6667           Feld[xx][yy] = activated_magic_wall;
6668       }
6669
6670       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6671       game.magic_wall_active = TRUE;
6672
6673       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6674                             SND_MAGIC_WALL_ACTIVATING :
6675                             smashed == EL_BD_MAGIC_WALL ?
6676                             SND_BD_MAGIC_WALL_ACTIVATING :
6677                             SND_DC_MAGIC_WALL_ACTIVATING));
6678     }
6679
6680     if (IS_PLAYER(x, y + 1))
6681     {
6682       if (CAN_SMASH_PLAYER(element))
6683       {
6684         KillPlayerUnlessEnemyProtected(x, y + 1);
6685         return;
6686       }
6687     }
6688     else if (smashed == EL_PENGUIN)
6689     {
6690       if (CAN_SMASH_PLAYER(element))
6691       {
6692         Bang(x, y + 1);
6693         return;
6694       }
6695     }
6696     else if (element == EL_BD_DIAMOND)
6697     {
6698       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6699       {
6700         Bang(x, y + 1);
6701         return;
6702       }
6703     }
6704     else if (((element == EL_SP_INFOTRON ||
6705                element == EL_SP_ZONK) &&
6706               (smashed == EL_SP_SNIKSNAK ||
6707                smashed == EL_SP_ELECTRON ||
6708                smashed == EL_SP_DISK_ORANGE)) ||
6709              (element == EL_SP_INFOTRON &&
6710               smashed == EL_SP_DISK_YELLOW))
6711     {
6712       Bang(x, y + 1);
6713       return;
6714     }
6715     else if (CAN_SMASH_EVERYTHING(element))
6716     {
6717       if (IS_CLASSIC_ENEMY(smashed) ||
6718           CAN_EXPLODE_SMASHED(smashed))
6719       {
6720         Bang(x, y + 1);
6721         return;
6722       }
6723       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6724       {
6725         if (smashed == EL_LAMP ||
6726             smashed == EL_LAMP_ACTIVE)
6727         {
6728           Bang(x, y + 1);
6729           return;
6730         }
6731         else if (smashed == EL_NUT)
6732         {
6733           Feld[x][y + 1] = EL_NUT_BREAKING;
6734           PlayLevelSound(x, y, SND_NUT_BREAKING);
6735           RaiseScoreElement(EL_NUT);
6736           return;
6737         }
6738         else if (smashed == EL_PEARL)
6739         {
6740           ResetGfxAnimation(x, y);
6741
6742           Feld[x][y + 1] = EL_PEARL_BREAKING;
6743           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6744           return;
6745         }
6746         else if (smashed == EL_DIAMOND)
6747         {
6748           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6749           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6750           return;
6751         }
6752         else if (IS_BELT_SWITCH(smashed))
6753         {
6754           ToggleBeltSwitch(x, y + 1);
6755         }
6756         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6757                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6758                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6759                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6760         {
6761           ToggleSwitchgateSwitch(x, y + 1);
6762         }
6763         else if (smashed == EL_LIGHT_SWITCH ||
6764                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6765         {
6766           ToggleLightSwitch(x, y + 1);
6767         }
6768         else
6769         {
6770 #if 0
6771           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6772 #endif
6773
6774           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6775
6776           CheckElementChangeBySide(x, y + 1, smashed, element,
6777                                    CE_SWITCHED, CH_SIDE_TOP);
6778           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6779                                             CH_SIDE_TOP);
6780         }
6781       }
6782       else
6783       {
6784         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6785       }
6786     }
6787   }
6788
6789   /* play sound of magic wall / mill */
6790   if (!last_line &&
6791       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6792        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6793        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6794   {
6795     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6796       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6797     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6798       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6799     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6800       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6801
6802     return;
6803   }
6804
6805   /* play sound of object that hits the ground */
6806   if (last_line || object_hit)
6807     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6808 }
6809
6810 inline static void TurnRoundExt(int x, int y)
6811 {
6812   static struct
6813   {
6814     int dx, dy;
6815   } move_xy[] =
6816   {
6817     {  0,  0 },
6818     { -1,  0 },
6819     { +1,  0 },
6820     {  0,  0 },
6821     {  0, -1 },
6822     {  0,  0 }, { 0, 0 }, { 0, 0 },
6823     {  0, +1 }
6824   };
6825   static struct
6826   {
6827     int left, right, back;
6828   } turn[] =
6829   {
6830     { 0,        0,              0        },
6831     { MV_DOWN,  MV_UP,          MV_RIGHT },
6832     { MV_UP,    MV_DOWN,        MV_LEFT  },
6833     { 0,        0,              0        },
6834     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6835     { 0,        0,              0        },
6836     { 0,        0,              0        },
6837     { 0,        0,              0        },
6838     { MV_RIGHT, MV_LEFT,        MV_UP    }
6839   };
6840
6841   int element = Feld[x][y];
6842   int move_pattern = element_info[element].move_pattern;
6843
6844   int old_move_dir = MovDir[x][y];
6845   int left_dir  = turn[old_move_dir].left;
6846   int right_dir = turn[old_move_dir].right;
6847   int back_dir  = turn[old_move_dir].back;
6848
6849   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6850   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6851   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6852   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6853
6854   int left_x  = x + left_dx,  left_y  = y + left_dy;
6855   int right_x = x + right_dx, right_y = y + right_dy;
6856   int move_x  = x + move_dx,  move_y  = y + move_dy;
6857
6858   int xx, yy;
6859
6860   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6861   {
6862     TestIfBadThingTouchesOtherBadThing(x, y);
6863
6864     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6865       MovDir[x][y] = right_dir;
6866     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6867       MovDir[x][y] = left_dir;
6868
6869     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6870       MovDelay[x][y] = 9;
6871     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6872       MovDelay[x][y] = 1;
6873   }
6874   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6875   {
6876     TestIfBadThingTouchesOtherBadThing(x, y);
6877
6878     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6879       MovDir[x][y] = left_dir;
6880     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6881       MovDir[x][y] = right_dir;
6882
6883     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6884       MovDelay[x][y] = 9;
6885     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6886       MovDelay[x][y] = 1;
6887   }
6888   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6889   {
6890     TestIfBadThingTouchesOtherBadThing(x, y);
6891
6892     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6893       MovDir[x][y] = left_dir;
6894     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6895       MovDir[x][y] = right_dir;
6896
6897     if (MovDir[x][y] != old_move_dir)
6898       MovDelay[x][y] = 9;
6899   }
6900   else if (element == EL_YAMYAM)
6901   {
6902     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6903     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6904
6905     if (can_turn_left && can_turn_right)
6906       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6907     else if (can_turn_left)
6908       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6909     else if (can_turn_right)
6910       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6911     else
6912       MovDir[x][y] = back_dir;
6913
6914     MovDelay[x][y] = 16 + 16 * RND(3);
6915   }
6916   else if (element == EL_DARK_YAMYAM)
6917   {
6918     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6919                                                          left_x, left_y);
6920     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6921                                                          right_x, right_y);
6922
6923     if (can_turn_left && can_turn_right)
6924       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6925     else if (can_turn_left)
6926       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6927     else if (can_turn_right)
6928       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6929     else
6930       MovDir[x][y] = back_dir;
6931
6932     MovDelay[x][y] = 16 + 16 * RND(3);
6933   }
6934   else if (element == EL_PACMAN)
6935   {
6936     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6937     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6938
6939     if (can_turn_left && can_turn_right)
6940       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6941     else if (can_turn_left)
6942       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6943     else if (can_turn_right)
6944       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6945     else
6946       MovDir[x][y] = back_dir;
6947
6948     MovDelay[x][y] = 6 + RND(40);
6949   }
6950   else if (element == EL_PIG)
6951   {
6952     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6953     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6954     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6955     boolean should_turn_left, should_turn_right, should_move_on;
6956     int rnd_value = 24;
6957     int rnd = RND(rnd_value);
6958
6959     should_turn_left = (can_turn_left &&
6960                         (!can_move_on ||
6961                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6962                                                    y + back_dy + left_dy)));
6963     should_turn_right = (can_turn_right &&
6964                          (!can_move_on ||
6965                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6966                                                     y + back_dy + right_dy)));
6967     should_move_on = (can_move_on &&
6968                       (!can_turn_left ||
6969                        !can_turn_right ||
6970                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6971                                                  y + move_dy + left_dy) ||
6972                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6973                                                  y + move_dy + right_dy)));
6974
6975     if (should_turn_left || should_turn_right || should_move_on)
6976     {
6977       if (should_turn_left && should_turn_right && should_move_on)
6978         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6979                         rnd < 2 * rnd_value / 3 ? right_dir :
6980                         old_move_dir);
6981       else if (should_turn_left && should_turn_right)
6982         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6983       else if (should_turn_left && should_move_on)
6984         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6985       else if (should_turn_right && should_move_on)
6986         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6987       else if (should_turn_left)
6988         MovDir[x][y] = left_dir;
6989       else if (should_turn_right)
6990         MovDir[x][y] = right_dir;
6991       else if (should_move_on)
6992         MovDir[x][y] = old_move_dir;
6993     }
6994     else if (can_move_on && rnd > rnd_value / 8)
6995       MovDir[x][y] = old_move_dir;
6996     else if (can_turn_left && can_turn_right)
6997       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6998     else if (can_turn_left && rnd > rnd_value / 8)
6999       MovDir[x][y] = left_dir;
7000     else if (can_turn_right && rnd > rnd_value/8)
7001       MovDir[x][y] = right_dir;
7002     else
7003       MovDir[x][y] = back_dir;
7004
7005     xx = x + move_xy[MovDir[x][y]].dx;
7006     yy = y + move_xy[MovDir[x][y]].dy;
7007
7008     if (!IN_LEV_FIELD(xx, yy) ||
7009         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7010       MovDir[x][y] = old_move_dir;
7011
7012     MovDelay[x][y] = 0;
7013   }
7014   else if (element == EL_DRAGON)
7015   {
7016     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7017     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7018     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7019     int rnd_value = 24;
7020     int rnd = RND(rnd_value);
7021
7022     if (can_move_on && rnd > rnd_value / 8)
7023       MovDir[x][y] = old_move_dir;
7024     else if (can_turn_left && can_turn_right)
7025       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7026     else if (can_turn_left && rnd > rnd_value / 8)
7027       MovDir[x][y] = left_dir;
7028     else if (can_turn_right && rnd > rnd_value / 8)
7029       MovDir[x][y] = right_dir;
7030     else
7031       MovDir[x][y] = back_dir;
7032
7033     xx = x + move_xy[MovDir[x][y]].dx;
7034     yy = y + move_xy[MovDir[x][y]].dy;
7035
7036     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7037       MovDir[x][y] = old_move_dir;
7038
7039     MovDelay[x][y] = 0;
7040   }
7041   else if (element == EL_MOLE)
7042   {
7043     boolean can_move_on =
7044       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7045                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7046                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7047     if (!can_move_on)
7048     {
7049       boolean can_turn_left =
7050         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7051                               IS_AMOEBOID(Feld[left_x][left_y])));
7052
7053       boolean can_turn_right =
7054         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7055                               IS_AMOEBOID(Feld[right_x][right_y])));
7056
7057       if (can_turn_left && can_turn_right)
7058         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7059       else if (can_turn_left)
7060         MovDir[x][y] = left_dir;
7061       else
7062         MovDir[x][y] = right_dir;
7063     }
7064
7065     if (MovDir[x][y] != old_move_dir)
7066       MovDelay[x][y] = 9;
7067   }
7068   else if (element == EL_BALLOON)
7069   {
7070     MovDir[x][y] = game.wind_direction;
7071     MovDelay[x][y] = 0;
7072   }
7073   else if (element == EL_SPRING)
7074   {
7075 #if USE_NEW_SPRING_BUMPER
7076     if (MovDir[x][y] & MV_HORIZONTAL)
7077     {
7078       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7079           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7080       {
7081         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7082         ResetGfxAnimation(move_x, move_y);
7083         TEST_DrawLevelField(move_x, move_y);
7084
7085         MovDir[x][y] = back_dir;
7086       }
7087       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7088                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7089         MovDir[x][y] = MV_NONE;
7090     }
7091 #else
7092     if (MovDir[x][y] & MV_HORIZONTAL &&
7093         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7094          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7095       MovDir[x][y] = MV_NONE;
7096 #endif
7097
7098     MovDelay[x][y] = 0;
7099   }
7100   else if (element == EL_ROBOT ||
7101            element == EL_SATELLITE ||
7102            element == EL_PENGUIN ||
7103            element == EL_EMC_ANDROID)
7104   {
7105     int attr_x = -1, attr_y = -1;
7106
7107     if (AllPlayersGone)
7108     {
7109       attr_x = ExitX;
7110       attr_y = ExitY;
7111     }
7112     else
7113     {
7114       int i;
7115
7116       for (i = 0; i < MAX_PLAYERS; i++)
7117       {
7118         struct PlayerInfo *player = &stored_player[i];
7119         int jx = player->jx, jy = player->jy;
7120
7121         if (!player->active)
7122           continue;
7123
7124         if (attr_x == -1 ||
7125             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7126         {
7127           attr_x = jx;
7128           attr_y = jy;
7129         }
7130       }
7131     }
7132
7133     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7134         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7135          game.engine_version < VERSION_IDENT(3,1,0,0)))
7136     {
7137       attr_x = ZX;
7138       attr_y = ZY;
7139     }
7140
7141     if (element == EL_PENGUIN)
7142     {
7143       int i;
7144       static int xy[4][2] =
7145       {
7146         { 0, -1 },
7147         { -1, 0 },
7148         { +1, 0 },
7149         { 0, +1 }
7150       };
7151
7152       for (i = 0; i < NUM_DIRECTIONS; i++)
7153       {
7154         int ex = x + xy[i][0];
7155         int ey = y + xy[i][1];
7156
7157         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7158                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7159                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7160                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7161         {
7162           attr_x = ex;
7163           attr_y = ey;
7164           break;
7165         }
7166       }
7167     }
7168
7169     MovDir[x][y] = MV_NONE;
7170     if (attr_x < x)
7171       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7172     else if (attr_x > x)
7173       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7174     if (attr_y < y)
7175       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7176     else if (attr_y > y)
7177       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7178
7179     if (element == EL_ROBOT)
7180     {
7181       int newx, newy;
7182
7183       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7184         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7185       Moving2Blocked(x, y, &newx, &newy);
7186
7187       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7188         MovDelay[x][y] = 8 + 8 * !RND(3);
7189       else
7190         MovDelay[x][y] = 16;
7191     }
7192     else if (element == EL_PENGUIN)
7193     {
7194       int newx, newy;
7195
7196       MovDelay[x][y] = 1;
7197
7198       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7199       {
7200         boolean first_horiz = RND(2);
7201         int new_move_dir = MovDir[x][y];
7202
7203         MovDir[x][y] =
7204           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205         Moving2Blocked(x, y, &newx, &newy);
7206
7207         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7208           return;
7209
7210         MovDir[x][y] =
7211           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212         Moving2Blocked(x, y, &newx, &newy);
7213
7214         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7215           return;
7216
7217         MovDir[x][y] = old_move_dir;
7218         return;
7219       }
7220     }
7221     else if (element == EL_SATELLITE)
7222     {
7223       int newx, newy;
7224
7225       MovDelay[x][y] = 1;
7226
7227       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7228       {
7229         boolean first_horiz = RND(2);
7230         int new_move_dir = MovDir[x][y];
7231
7232         MovDir[x][y] =
7233           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7234         Moving2Blocked(x, y, &newx, &newy);
7235
7236         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7237           return;
7238
7239         MovDir[x][y] =
7240           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7241         Moving2Blocked(x, y, &newx, &newy);
7242
7243         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7244           return;
7245
7246         MovDir[x][y] = old_move_dir;
7247         return;
7248       }
7249     }
7250     else if (element == EL_EMC_ANDROID)
7251     {
7252       static int check_pos[16] =
7253       {
7254         -1,             /*  0 => (invalid)          */
7255         7,              /*  1 => MV_LEFT            */
7256         3,              /*  2 => MV_RIGHT           */
7257         -1,             /*  3 => (invalid)          */
7258         1,              /*  4 =>            MV_UP   */
7259         0,              /*  5 => MV_LEFT  | MV_UP   */
7260         2,              /*  6 => MV_RIGHT | MV_UP   */
7261         -1,             /*  7 => (invalid)          */
7262         5,              /*  8 =>            MV_DOWN */
7263         6,              /*  9 => MV_LEFT  | MV_DOWN */
7264         4,              /* 10 => MV_RIGHT | MV_DOWN */
7265         -1,             /* 11 => (invalid)          */
7266         -1,             /* 12 => (invalid)          */
7267         -1,             /* 13 => (invalid)          */
7268         -1,             /* 14 => (invalid)          */
7269         -1,             /* 15 => (invalid)          */
7270       };
7271       static struct
7272       {
7273         int dx, dy;
7274         int dir;
7275       } check_xy[8] =
7276       {
7277         { -1, -1,       MV_LEFT  | MV_UP   },
7278         {  0, -1,                  MV_UP   },
7279         { +1, -1,       MV_RIGHT | MV_UP   },
7280         { +1,  0,       MV_RIGHT           },
7281         { +1, +1,       MV_RIGHT | MV_DOWN },
7282         {  0, +1,                  MV_DOWN },
7283         { -1, +1,       MV_LEFT  | MV_DOWN },
7284         { -1,  0,       MV_LEFT            },
7285       };
7286       int start_pos, check_order;
7287       boolean can_clone = FALSE;
7288       int i;
7289
7290       /* check if there is any free field around current position */
7291       for (i = 0; i < 8; i++)
7292       {
7293         int newx = x + check_xy[i].dx;
7294         int newy = y + check_xy[i].dy;
7295
7296         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7297         {
7298           can_clone = TRUE;
7299
7300           break;
7301         }
7302       }
7303
7304       if (can_clone)            /* randomly find an element to clone */
7305       {
7306         can_clone = FALSE;
7307
7308         start_pos = check_pos[RND(8)];
7309         check_order = (RND(2) ? -1 : +1);
7310
7311         for (i = 0; i < 8; i++)
7312         {
7313           int pos_raw = start_pos + i * check_order;
7314           int pos = (pos_raw + 8) % 8;
7315           int newx = x + check_xy[pos].dx;
7316           int newy = y + check_xy[pos].dy;
7317
7318           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7319           {
7320             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7321             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7322
7323             Store[x][y] = Feld[newx][newy];
7324
7325             can_clone = TRUE;
7326
7327             break;
7328           }
7329         }
7330       }
7331
7332       if (can_clone)            /* randomly find a direction to move */
7333       {
7334         can_clone = FALSE;
7335
7336         start_pos = check_pos[RND(8)];
7337         check_order = (RND(2) ? -1 : +1);
7338
7339         for (i = 0; i < 8; i++)
7340         {
7341           int pos_raw = start_pos + i * check_order;
7342           int pos = (pos_raw + 8) % 8;
7343           int newx = x + check_xy[pos].dx;
7344           int newy = y + check_xy[pos].dy;
7345           int new_move_dir = check_xy[pos].dir;
7346
7347           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7348           {
7349             MovDir[x][y] = new_move_dir;
7350             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7351
7352             can_clone = TRUE;
7353
7354             break;
7355           }
7356         }
7357       }
7358
7359       if (can_clone)            /* cloning and moving successful */
7360         return;
7361
7362       /* cannot clone -- try to move towards player */
7363
7364       start_pos = check_pos[MovDir[x][y] & 0x0f];
7365       check_order = (RND(2) ? -1 : +1);
7366
7367       for (i = 0; i < 3; i++)
7368       {
7369         /* first check start_pos, then previous/next or (next/previous) pos */
7370         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7371         int pos = (pos_raw + 8) % 8;
7372         int newx = x + check_xy[pos].dx;
7373         int newy = y + check_xy[pos].dy;
7374         int new_move_dir = check_xy[pos].dir;
7375
7376         if (IS_PLAYER(newx, newy))
7377           break;
7378
7379         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7380         {
7381           MovDir[x][y] = new_move_dir;
7382           MovDelay[x][y] = level.android_move_time * 8 + 1;
7383
7384           break;
7385         }
7386       }
7387     }
7388   }
7389   else if (move_pattern == MV_TURNING_LEFT ||
7390            move_pattern == MV_TURNING_RIGHT ||
7391            move_pattern == MV_TURNING_LEFT_RIGHT ||
7392            move_pattern == MV_TURNING_RIGHT_LEFT ||
7393            move_pattern == MV_TURNING_RANDOM ||
7394            move_pattern == MV_ALL_DIRECTIONS)
7395   {
7396     boolean can_turn_left =
7397       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7398     boolean can_turn_right =
7399       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7400
7401     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7402       return;
7403
7404     if (move_pattern == MV_TURNING_LEFT)
7405       MovDir[x][y] = left_dir;
7406     else if (move_pattern == MV_TURNING_RIGHT)
7407       MovDir[x][y] = right_dir;
7408     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7409       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7410     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7411       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7412     else if (move_pattern == MV_TURNING_RANDOM)
7413       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7414                       can_turn_right && !can_turn_left ? right_dir :
7415                       RND(2) ? left_dir : right_dir);
7416     else if (can_turn_left && can_turn_right)
7417       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7418     else if (can_turn_left)
7419       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7420     else if (can_turn_right)
7421       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7422     else
7423       MovDir[x][y] = back_dir;
7424
7425     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7426   }
7427   else if (move_pattern == MV_HORIZONTAL ||
7428            move_pattern == MV_VERTICAL)
7429   {
7430     if (move_pattern & old_move_dir)
7431       MovDir[x][y] = back_dir;
7432     else if (move_pattern == MV_HORIZONTAL)
7433       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7434     else if (move_pattern == MV_VERTICAL)
7435       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7436
7437     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7438   }
7439   else if (move_pattern & MV_ANY_DIRECTION)
7440   {
7441     MovDir[x][y] = move_pattern;
7442     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7443   }
7444   else if (move_pattern & MV_WIND_DIRECTION)
7445   {
7446     MovDir[x][y] = game.wind_direction;
7447     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7448   }
7449   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7450   {
7451     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7452       MovDir[x][y] = left_dir;
7453     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7454       MovDir[x][y] = right_dir;
7455
7456     if (MovDir[x][y] != old_move_dir)
7457       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7458   }
7459   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7460   {
7461     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7462       MovDir[x][y] = right_dir;
7463     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7464       MovDir[x][y] = left_dir;
7465
7466     if (MovDir[x][y] != old_move_dir)
7467       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7468   }
7469   else if (move_pattern == MV_TOWARDS_PLAYER ||
7470            move_pattern == MV_AWAY_FROM_PLAYER)
7471   {
7472     int attr_x = -1, attr_y = -1;
7473     int newx, newy;
7474     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7475
7476     if (AllPlayersGone)
7477     {
7478       attr_x = ExitX;
7479       attr_y = ExitY;
7480     }
7481     else
7482     {
7483       int i;
7484
7485       for (i = 0; i < MAX_PLAYERS; i++)
7486       {
7487         struct PlayerInfo *player = &stored_player[i];
7488         int jx = player->jx, jy = player->jy;
7489
7490         if (!player->active)
7491           continue;
7492
7493         if (attr_x == -1 ||
7494             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7495         {
7496           attr_x = jx;
7497           attr_y = jy;
7498         }
7499       }
7500     }
7501
7502     MovDir[x][y] = MV_NONE;
7503     if (attr_x < x)
7504       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7505     else if (attr_x > x)
7506       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7507     if (attr_y < y)
7508       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7509     else if (attr_y > y)
7510       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7511
7512     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7513
7514     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7515     {
7516       boolean first_horiz = RND(2);
7517       int new_move_dir = MovDir[x][y];
7518
7519       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7520       {
7521         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7522         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7523
7524         return;
7525       }
7526
7527       MovDir[x][y] =
7528         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529       Moving2Blocked(x, y, &newx, &newy);
7530
7531       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7532         return;
7533
7534       MovDir[x][y] =
7535         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7536       Moving2Blocked(x, y, &newx, &newy);
7537
7538       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7539         return;
7540
7541       MovDir[x][y] = old_move_dir;
7542     }
7543   }
7544   else if (move_pattern == MV_WHEN_PUSHED ||
7545            move_pattern == MV_WHEN_DROPPED)
7546   {
7547     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7548       MovDir[x][y] = MV_NONE;
7549
7550     MovDelay[x][y] = 0;
7551   }
7552   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7553   {
7554     static int test_xy[7][2] =
7555     {
7556       { 0, -1 },
7557       { -1, 0 },
7558       { +1, 0 },
7559       { 0, +1 },
7560       { 0, -1 },
7561       { -1, 0 },
7562       { +1, 0 },
7563     };
7564     static int test_dir[7] =
7565     {
7566       MV_UP,
7567       MV_LEFT,
7568       MV_RIGHT,
7569       MV_DOWN,
7570       MV_UP,
7571       MV_LEFT,
7572       MV_RIGHT,
7573     };
7574     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7575     int move_preference = -1000000;     /* start with very low preference */
7576     int new_move_dir = MV_NONE;
7577     int start_test = RND(4);
7578     int i;
7579
7580     for (i = 0; i < NUM_DIRECTIONS; i++)
7581     {
7582       int move_dir = test_dir[start_test + i];
7583       int move_dir_preference;
7584
7585       xx = x + test_xy[start_test + i][0];
7586       yy = y + test_xy[start_test + i][1];
7587
7588       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7589           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7590       {
7591         new_move_dir = move_dir;
7592
7593         break;
7594       }
7595
7596       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7597         continue;
7598
7599       move_dir_preference = -1 * RunnerVisit[xx][yy];
7600       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7601         move_dir_preference = PlayerVisit[xx][yy];
7602
7603       if (move_dir_preference > move_preference)
7604       {
7605         /* prefer field that has not been visited for the longest time */
7606         move_preference = move_dir_preference;
7607         new_move_dir = move_dir;
7608       }
7609       else if (move_dir_preference == move_preference &&
7610                move_dir == old_move_dir)
7611       {
7612         /* prefer last direction when all directions are preferred equally */
7613         move_preference = move_dir_preference;
7614         new_move_dir = move_dir;
7615       }
7616     }
7617
7618     MovDir[x][y] = new_move_dir;
7619     if (old_move_dir != new_move_dir)
7620       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7621   }
7622 }
7623
7624 static void TurnRound(int x, int y)
7625 {
7626   int direction = MovDir[x][y];
7627
7628   TurnRoundExt(x, y);
7629
7630   GfxDir[x][y] = MovDir[x][y];
7631
7632   if (direction != MovDir[x][y])
7633     GfxFrame[x][y] = 0;
7634
7635   if (MovDelay[x][y])
7636     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7637
7638   ResetGfxFrame(x, y, FALSE);
7639 }
7640
7641 static boolean JustBeingPushed(int x, int y)
7642 {
7643   int i;
7644
7645   for (i = 0; i < MAX_PLAYERS; i++)
7646   {
7647     struct PlayerInfo *player = &stored_player[i];
7648
7649     if (player->active && player->is_pushing && player->MovPos)
7650     {
7651       int next_jx = player->jx + (player->jx - player->last_jx);
7652       int next_jy = player->jy + (player->jy - player->last_jy);
7653
7654       if (x == next_jx && y == next_jy)
7655         return TRUE;
7656     }
7657   }
7658
7659   return FALSE;
7660 }
7661
7662 void StartMoving(int x, int y)
7663 {
7664   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7665   int element = Feld[x][y];
7666
7667   if (Stop[x][y])
7668     return;
7669
7670   if (MovDelay[x][y] == 0)
7671     GfxAction[x][y] = ACTION_DEFAULT;
7672
7673   if (CAN_FALL(element) && y < lev_fieldy - 1)
7674   {
7675     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7676         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7677       if (JustBeingPushed(x, y))
7678         return;
7679
7680     if (element == EL_QUICKSAND_FULL)
7681     {
7682       if (IS_FREE(x, y + 1))
7683       {
7684         InitMovingField(x, y, MV_DOWN);
7685         started_moving = TRUE;
7686
7687         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7688 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7689         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7690           Store[x][y] = EL_ROCK;
7691 #else
7692         Store[x][y] = EL_ROCK;
7693 #endif
7694
7695         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7696       }
7697       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7698       {
7699         if (!MovDelay[x][y])
7700         {
7701           MovDelay[x][y] = TILEY + 1;
7702
7703           ResetGfxAnimation(x, y);
7704           ResetGfxAnimation(x, y + 1);
7705         }
7706
7707         if (MovDelay[x][y])
7708         {
7709           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7710           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7711
7712           MovDelay[x][y]--;
7713           if (MovDelay[x][y])
7714             return;
7715         }
7716
7717         Feld[x][y] = EL_QUICKSAND_EMPTY;
7718         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7719         Store[x][y + 1] = Store[x][y];
7720         Store[x][y] = 0;
7721
7722         PlayLevelSoundAction(x, y, ACTION_FILLING);
7723       }
7724       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7725       {
7726         if (!MovDelay[x][y])
7727         {
7728           MovDelay[x][y] = TILEY + 1;
7729
7730           ResetGfxAnimation(x, y);
7731           ResetGfxAnimation(x, y + 1);
7732         }
7733
7734         if (MovDelay[x][y])
7735         {
7736           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7737           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7738
7739           MovDelay[x][y]--;
7740           if (MovDelay[x][y])
7741             return;
7742         }
7743
7744         Feld[x][y] = EL_QUICKSAND_EMPTY;
7745         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7746         Store[x][y + 1] = Store[x][y];
7747         Store[x][y] = 0;
7748
7749         PlayLevelSoundAction(x, y, ACTION_FILLING);
7750       }
7751     }
7752     else if (element == EL_QUICKSAND_FAST_FULL)
7753     {
7754       if (IS_FREE(x, y + 1))
7755       {
7756         InitMovingField(x, y, MV_DOWN);
7757         started_moving = TRUE;
7758
7759         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7760 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7761         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7762           Store[x][y] = EL_ROCK;
7763 #else
7764         Store[x][y] = EL_ROCK;
7765 #endif
7766
7767         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7768       }
7769       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7770       {
7771         if (!MovDelay[x][y])
7772         {
7773           MovDelay[x][y] = TILEY + 1;
7774
7775           ResetGfxAnimation(x, y);
7776           ResetGfxAnimation(x, y + 1);
7777         }
7778
7779         if (MovDelay[x][y])
7780         {
7781           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7782           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7783
7784           MovDelay[x][y]--;
7785           if (MovDelay[x][y])
7786             return;
7787         }
7788
7789         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7791         Store[x][y + 1] = Store[x][y];
7792         Store[x][y] = 0;
7793
7794         PlayLevelSoundAction(x, y, ACTION_FILLING);
7795       }
7796       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7797       {
7798         if (!MovDelay[x][y])
7799         {
7800           MovDelay[x][y] = TILEY + 1;
7801
7802           ResetGfxAnimation(x, y);
7803           ResetGfxAnimation(x, y + 1);
7804         }
7805
7806         if (MovDelay[x][y])
7807         {
7808           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7809           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7810
7811           MovDelay[x][y]--;
7812           if (MovDelay[x][y])
7813             return;
7814         }
7815
7816         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7817         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7818         Store[x][y + 1] = Store[x][y];
7819         Store[x][y] = 0;
7820
7821         PlayLevelSoundAction(x, y, ACTION_FILLING);
7822       }
7823     }
7824     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7825              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7826     {
7827       InitMovingField(x, y, MV_DOWN);
7828       started_moving = TRUE;
7829
7830       Feld[x][y] = EL_QUICKSAND_FILLING;
7831       Store[x][y] = element;
7832
7833       PlayLevelSoundAction(x, y, ACTION_FILLING);
7834     }
7835     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7836              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7837     {
7838       InitMovingField(x, y, MV_DOWN);
7839       started_moving = TRUE;
7840
7841       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7842       Store[x][y] = element;
7843
7844       PlayLevelSoundAction(x, y, ACTION_FILLING);
7845     }
7846     else if (element == EL_MAGIC_WALL_FULL)
7847     {
7848       if (IS_FREE(x, y + 1))
7849       {
7850         InitMovingField(x, y, MV_DOWN);
7851         started_moving = TRUE;
7852
7853         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7854         Store[x][y] = EL_CHANGED(Store[x][y]);
7855       }
7856       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7857       {
7858         if (!MovDelay[x][y])
7859           MovDelay[x][y] = TILEY/4 + 1;
7860
7861         if (MovDelay[x][y])
7862         {
7863           MovDelay[x][y]--;
7864           if (MovDelay[x][y])
7865             return;
7866         }
7867
7868         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7869         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7870         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7871         Store[x][y] = 0;
7872       }
7873     }
7874     else if (element == EL_BD_MAGIC_WALL_FULL)
7875     {
7876       if (IS_FREE(x, y + 1))
7877       {
7878         InitMovingField(x, y, MV_DOWN);
7879         started_moving = TRUE;
7880
7881         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7882         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7883       }
7884       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7885       {
7886         if (!MovDelay[x][y])
7887           MovDelay[x][y] = TILEY/4 + 1;
7888
7889         if (MovDelay[x][y])
7890         {
7891           MovDelay[x][y]--;
7892           if (MovDelay[x][y])
7893             return;
7894         }
7895
7896         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7897         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7898         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7899         Store[x][y] = 0;
7900       }
7901     }
7902     else if (element == EL_DC_MAGIC_WALL_FULL)
7903     {
7904       if (IS_FREE(x, y + 1))
7905       {
7906         InitMovingField(x, y, MV_DOWN);
7907         started_moving = TRUE;
7908
7909         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7910         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7911       }
7912       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7913       {
7914         if (!MovDelay[x][y])
7915           MovDelay[x][y] = TILEY/4 + 1;
7916
7917         if (MovDelay[x][y])
7918         {
7919           MovDelay[x][y]--;
7920           if (MovDelay[x][y])
7921             return;
7922         }
7923
7924         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7925         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7926         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7927         Store[x][y] = 0;
7928       }
7929     }
7930     else if ((CAN_PASS_MAGIC_WALL(element) &&
7931               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7932                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7933              (CAN_PASS_DC_MAGIC_WALL(element) &&
7934               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7935
7936     {
7937       InitMovingField(x, y, MV_DOWN);
7938       started_moving = TRUE;
7939
7940       Feld[x][y] =
7941         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7942          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7943          EL_DC_MAGIC_WALL_FILLING);
7944       Store[x][y] = element;
7945     }
7946     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7947     {
7948       SplashAcid(x, y + 1);
7949
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Store[x][y] = EL_ACID;
7954     }
7955     else if (
7956 #if USE_FIX_IMPACT_COLLISION
7957              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7958               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7959 #else
7960              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7961               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7962 #endif
7963              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7964               CAN_FALL(element) && WasJustFalling[x][y] &&
7965               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7966
7967              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7968               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7969               (Feld[x][y + 1] == EL_BLOCKED)))
7970     {
7971       /* this is needed for a special case not covered by calling "Impact()"
7972          from "ContinueMoving()": if an element moves to a tile directly below
7973          another element which was just falling on that tile (which was empty
7974          in the previous frame), the falling element above would just stop
7975          instead of smashing the element below (in previous version, the above
7976          element was just checked for "moving" instead of "falling", resulting
7977          in incorrect smashes caused by horizontal movement of the above
7978          element; also, the case of the player being the element to smash was
7979          simply not covered here... :-/ ) */
7980
7981       CheckCollision[x][y] = 0;
7982       CheckImpact[x][y] = 0;
7983
7984       Impact(x, y);
7985     }
7986     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7987     {
7988       if (MovDir[x][y] == MV_NONE)
7989       {
7990         InitMovingField(x, y, MV_DOWN);
7991         started_moving = TRUE;
7992       }
7993     }
7994     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7995     {
7996       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7997         MovDir[x][y] = MV_DOWN;
7998
7999       InitMovingField(x, y, MV_DOWN);
8000       started_moving = TRUE;
8001     }
8002     else if (element == EL_AMOEBA_DROP)
8003     {
8004       Feld[x][y] = EL_AMOEBA_GROWING;
8005       Store[x][y] = EL_AMOEBA_WET;
8006     }
8007     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8008               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8009              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8010              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8011     {
8012       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8013                                 (IS_FREE(x - 1, y + 1) ||
8014                                  Feld[x - 1][y + 1] == EL_ACID));
8015       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8016                                 (IS_FREE(x + 1, y + 1) ||
8017                                  Feld[x + 1][y + 1] == EL_ACID));
8018       boolean can_fall_any  = (can_fall_left || can_fall_right);
8019       boolean can_fall_both = (can_fall_left && can_fall_right);
8020       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8021
8022 #if USE_NEW_ALL_SLIPPERY
8023       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8024       {
8025         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8026           can_fall_right = FALSE;
8027         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8028           can_fall_left = FALSE;
8029         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8030           can_fall_right = FALSE;
8031         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8032           can_fall_left = FALSE;
8033
8034         can_fall_any  = (can_fall_left || can_fall_right);
8035         can_fall_both = FALSE;
8036       }
8037 #else
8038       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8039       {
8040         if (slippery_type == SLIPPERY_ONLY_LEFT)
8041           can_fall_right = FALSE;
8042         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8043           can_fall_left = FALSE;
8044         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8045           can_fall_right = FALSE;
8046         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8047           can_fall_left = FALSE;
8048
8049         can_fall_any  = (can_fall_left || can_fall_right);
8050         can_fall_both = (can_fall_left && can_fall_right);
8051       }
8052 #endif
8053
8054 #if USE_NEW_ALL_SLIPPERY
8055 #else
8056 #if USE_NEW_SP_SLIPPERY
8057       /* !!! better use the same properties as for custom elements here !!! */
8058       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8059                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8060       {
8061         can_fall_right = FALSE;         /* slip down on left side */
8062         can_fall_both = FALSE;
8063       }
8064 #endif
8065 #endif
8066
8067 #if USE_NEW_ALL_SLIPPERY
8068       if (can_fall_both)
8069       {
8070         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8071           can_fall_right = FALSE;       /* slip down on left side */
8072         else
8073           can_fall_left = !(can_fall_right = RND(2));
8074
8075         can_fall_both = FALSE;
8076       }
8077 #else
8078       if (can_fall_both)
8079       {
8080         if (game.emulation == EMU_BOULDERDASH ||
8081             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8082           can_fall_right = FALSE;       /* slip down on left side */
8083         else
8084           can_fall_left = !(can_fall_right = RND(2));
8085
8086         can_fall_both = FALSE;
8087       }
8088 #endif
8089
8090       if (can_fall_any)
8091       {
8092         /* if not determined otherwise, prefer left side for slipping down */
8093         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8094         started_moving = TRUE;
8095       }
8096     }
8097 #if 0
8098     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8099 #else
8100     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8101 #endif
8102     {
8103       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8104       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8105       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8106       int belt_dir = game.belt_dir[belt_nr];
8107
8108       if ((belt_dir == MV_LEFT  && left_is_free) ||
8109           (belt_dir == MV_RIGHT && right_is_free))
8110       {
8111         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8112
8113         InitMovingField(x, y, belt_dir);
8114         started_moving = TRUE;
8115
8116         Pushed[x][y] = TRUE;
8117         Pushed[nextx][y] = TRUE;
8118
8119         GfxAction[x][y] = ACTION_DEFAULT;
8120       }
8121       else
8122       {
8123         MovDir[x][y] = 0;       /* if element was moving, stop it */
8124       }
8125     }
8126   }
8127
8128   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8129 #if 0
8130   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8131 #else
8132   if (CAN_MOVE(element) && !started_moving)
8133 #endif
8134   {
8135     int move_pattern = element_info[element].move_pattern;
8136     int newx, newy;
8137
8138 #if 0
8139 #if DEBUG
8140     if (MovDir[x][y] == MV_NONE)
8141     {
8142       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8143              x, y, element, element_info[element].token_name);
8144       printf("StartMoving(): This should never happen!\n");
8145     }
8146 #endif
8147 #endif
8148
8149     Moving2Blocked(x, y, &newx, &newy);
8150
8151     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8152       return;
8153
8154     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8155         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8156     {
8157       WasJustMoving[x][y] = 0;
8158       CheckCollision[x][y] = 0;
8159
8160       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8161
8162       if (Feld[x][y] != element)        /* element has changed */
8163         return;
8164     }
8165
8166     if (!MovDelay[x][y])        /* start new movement phase */
8167     {
8168       /* all objects that can change their move direction after each step
8169          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8170
8171       if (element != EL_YAMYAM &&
8172           element != EL_DARK_YAMYAM &&
8173           element != EL_PACMAN &&
8174           !(move_pattern & MV_ANY_DIRECTION) &&
8175           move_pattern != MV_TURNING_LEFT &&
8176           move_pattern != MV_TURNING_RIGHT &&
8177           move_pattern != MV_TURNING_LEFT_RIGHT &&
8178           move_pattern != MV_TURNING_RIGHT_LEFT &&
8179           move_pattern != MV_TURNING_RANDOM)
8180       {
8181         TurnRound(x, y);
8182
8183         if (MovDelay[x][y] && (element == EL_BUG ||
8184                                element == EL_SPACESHIP ||
8185                                element == EL_SP_SNIKSNAK ||
8186                                element == EL_SP_ELECTRON ||
8187                                element == EL_MOLE))
8188           TEST_DrawLevelField(x, y);
8189       }
8190     }
8191
8192     if (MovDelay[x][y])         /* wait some time before next movement */
8193     {
8194       MovDelay[x][y]--;
8195
8196       if (element == EL_ROBOT ||
8197           element == EL_YAMYAM ||
8198           element == EL_DARK_YAMYAM)
8199       {
8200         DrawLevelElementAnimationIfNeeded(x, y, element);
8201         PlayLevelSoundAction(x, y, ACTION_WAITING);
8202       }
8203       else if (element == EL_SP_ELECTRON)
8204         DrawLevelElementAnimationIfNeeded(x, y, element);
8205       else if (element == EL_DRAGON)
8206       {
8207         int i;
8208         int dir = MovDir[x][y];
8209         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8210         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8211         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8212                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8213                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8214                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8215         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8216
8217         GfxAction[x][y] = ACTION_ATTACKING;
8218
8219         if (IS_PLAYER(x, y))
8220           DrawPlayerField(x, y);
8221         else
8222           TEST_DrawLevelField(x, y);
8223
8224         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8225
8226         for (i = 1; i <= 3; i++)
8227         {
8228           int xx = x + i * dx;
8229           int yy = y + i * dy;
8230           int sx = SCREENX(xx);
8231           int sy = SCREENY(yy);
8232           int flame_graphic = graphic + (i - 1);
8233
8234           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8235             break;
8236
8237           if (MovDelay[x][y])
8238           {
8239             int flamed = MovingOrBlocked2Element(xx, yy);
8240
8241             /* !!! */
8242 #if 0
8243             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8244               Bang(xx, yy);
8245             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8246               RemoveMovingField(xx, yy);
8247             else
8248               RemoveField(xx, yy);
8249 #else
8250             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8251               Bang(xx, yy);
8252             else
8253               RemoveMovingField(xx, yy);
8254 #endif
8255
8256             ChangeDelay[xx][yy] = 0;
8257
8258             Feld[xx][yy] = EL_FLAMES;
8259
8260             if (IN_SCR_FIELD(sx, sy))
8261             {
8262               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8263               DrawGraphic(sx, sy, flame_graphic, frame);
8264             }
8265           }
8266           else
8267           {
8268             if (Feld[xx][yy] == EL_FLAMES)
8269               Feld[xx][yy] = EL_EMPTY;
8270             TEST_DrawLevelField(xx, yy);
8271           }
8272         }
8273       }
8274
8275       if (MovDelay[x][y])       /* element still has to wait some time */
8276       {
8277         PlayLevelSoundAction(x, y, ACTION_WAITING);
8278
8279         return;
8280       }
8281     }
8282
8283     /* now make next step */
8284
8285     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8286
8287     if (DONT_COLLIDE_WITH(element) &&
8288         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8289         !PLAYER_ENEMY_PROTECTED(newx, newy))
8290     {
8291       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8292
8293       return;
8294     }
8295
8296     else if (CAN_MOVE_INTO_ACID(element) &&
8297              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8298              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8299              (MovDir[x][y] == MV_DOWN ||
8300               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8301     {
8302       SplashAcid(newx, newy);
8303       Store[x][y] = EL_ACID;
8304     }
8305     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8306     {
8307       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8308           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8309           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8310           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8311       {
8312         RemoveField(x, y);
8313         TEST_DrawLevelField(x, y);
8314
8315         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8316         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8317           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8318
8319         local_player->friends_still_needed--;
8320         if (!local_player->friends_still_needed &&
8321             !local_player->GameOver && AllPlayersGone)
8322           PlayerWins(local_player);
8323
8324         return;
8325       }
8326       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8327       {
8328         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8329           TEST_DrawLevelField(newx, newy);
8330         else
8331           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8332       }
8333       else if (!IS_FREE(newx, newy))
8334       {
8335         GfxAction[x][y] = ACTION_WAITING;
8336
8337         if (IS_PLAYER(x, y))
8338           DrawPlayerField(x, y);
8339         else
8340           TEST_DrawLevelField(x, y);
8341
8342         return;
8343       }
8344     }
8345     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8346     {
8347       if (IS_FOOD_PIG(Feld[newx][newy]))
8348       {
8349         if (IS_MOVING(newx, newy))
8350           RemoveMovingField(newx, newy);
8351         else
8352         {
8353           Feld[newx][newy] = EL_EMPTY;
8354           TEST_DrawLevelField(newx, newy);
8355         }
8356
8357         PlayLevelSound(x, y, SND_PIG_DIGGING);
8358       }
8359       else if (!IS_FREE(newx, newy))
8360       {
8361         if (IS_PLAYER(x, y))
8362           DrawPlayerField(x, y);
8363         else
8364           TEST_DrawLevelField(x, y);
8365
8366         return;
8367       }
8368     }
8369     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8370     {
8371       if (Store[x][y] != EL_EMPTY)
8372       {
8373         boolean can_clone = FALSE;
8374         int xx, yy;
8375
8376         /* check if element to clone is still there */
8377         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8378         {
8379           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8380           {
8381             can_clone = TRUE;
8382
8383             break;
8384           }
8385         }
8386
8387         /* cannot clone or target field not free anymore -- do not clone */
8388         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8389           Store[x][y] = EL_EMPTY;
8390       }
8391
8392       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8393       {
8394         if (IS_MV_DIAGONAL(MovDir[x][y]))
8395         {
8396           int diagonal_move_dir = MovDir[x][y];
8397           int stored = Store[x][y];
8398           int change_delay = 8;
8399           int graphic;
8400
8401           /* android is moving diagonally */
8402
8403           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8404
8405           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8406           GfxElement[x][y] = EL_EMC_ANDROID;
8407           GfxAction[x][y] = ACTION_SHRINKING;
8408           GfxDir[x][y] = diagonal_move_dir;
8409           ChangeDelay[x][y] = change_delay;
8410
8411           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8412                                    GfxDir[x][y]);
8413
8414           DrawLevelGraphicAnimation(x, y, graphic);
8415           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8416
8417           if (Feld[newx][newy] == EL_ACID)
8418           {
8419             SplashAcid(newx, newy);
8420
8421             return;
8422           }
8423
8424           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8425
8426           Store[newx][newy] = EL_EMC_ANDROID;
8427           GfxElement[newx][newy] = EL_EMC_ANDROID;
8428           GfxAction[newx][newy] = ACTION_GROWING;
8429           GfxDir[newx][newy] = diagonal_move_dir;
8430           ChangeDelay[newx][newy] = change_delay;
8431
8432           graphic = el_act_dir2img(GfxElement[newx][newy],
8433                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8434
8435           DrawLevelGraphicAnimation(newx, newy, graphic);
8436           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8437
8438           return;
8439         }
8440         else
8441         {
8442           Feld[newx][newy] = EL_EMPTY;
8443           TEST_DrawLevelField(newx, newy);
8444
8445           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8446         }
8447       }
8448       else if (!IS_FREE(newx, newy))
8449       {
8450 #if 0
8451         if (IS_PLAYER(x, y))
8452           DrawPlayerField(x, y);
8453         else
8454           TEST_DrawLevelField(x, y);
8455 #endif
8456
8457         return;
8458       }
8459     }
8460     else if (IS_CUSTOM_ELEMENT(element) &&
8461              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8462     {
8463 #if 1
8464       if (!DigFieldByCE(newx, newy, element))
8465         return;
8466 #else
8467       int new_element = Feld[newx][newy];
8468
8469       if (!IS_FREE(newx, newy))
8470       {
8471         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8472                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8473                       ACTION_BREAKING);
8474
8475         /* no element can dig solid indestructible elements */
8476         if (IS_INDESTRUCTIBLE(new_element) &&
8477             !IS_DIGGABLE(new_element) &&
8478             !IS_COLLECTIBLE(new_element))
8479           return;
8480
8481         if (AmoebaNr[newx][newy] &&
8482             (new_element == EL_AMOEBA_FULL ||
8483              new_element == EL_BD_AMOEBA ||
8484              new_element == EL_AMOEBA_GROWING))
8485         {
8486           AmoebaCnt[AmoebaNr[newx][newy]]--;
8487           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8488         }
8489
8490         if (IS_MOVING(newx, newy))
8491           RemoveMovingField(newx, newy);
8492         else
8493         {
8494           RemoveField(newx, newy);
8495           TEST_DrawLevelField(newx, newy);
8496         }
8497
8498         /* if digged element was about to explode, prevent the explosion */
8499         ExplodeField[newx][newy] = EX_TYPE_NONE;
8500
8501         PlayLevelSoundAction(x, y, action);
8502       }
8503
8504       Store[newx][newy] = EL_EMPTY;
8505
8506 #if 1
8507       /* this makes it possible to leave the removed element again */
8508       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8509         Store[newx][newy] = new_element;
8510 #else
8511       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8512       {
8513         int move_leave_element = element_info[element].move_leave_element;
8514
8515         /* this makes it possible to leave the removed element again */
8516         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8517                              new_element : move_leave_element);
8518       }
8519 #endif
8520
8521 #endif
8522
8523       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8524       {
8525         RunnerVisit[x][y] = FrameCounter;
8526         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8527       }
8528     }
8529     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8530     {
8531       if (!IS_FREE(newx, newy))
8532       {
8533         if (IS_PLAYER(x, y))
8534           DrawPlayerField(x, y);
8535         else
8536           TEST_DrawLevelField(x, y);
8537
8538         return;
8539       }
8540       else
8541       {
8542         boolean wanna_flame = !RND(10);
8543         int dx = newx - x, dy = newy - y;
8544         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8545         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8546         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8547                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8548         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8549                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8550
8551         if ((wanna_flame ||
8552              IS_CLASSIC_ENEMY(element1) ||
8553              IS_CLASSIC_ENEMY(element2)) &&
8554             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8555             element1 != EL_FLAMES && element2 != EL_FLAMES)
8556         {
8557           ResetGfxAnimation(x, y);
8558           GfxAction[x][y] = ACTION_ATTACKING;
8559
8560           if (IS_PLAYER(x, y))
8561             DrawPlayerField(x, y);
8562           else
8563             TEST_DrawLevelField(x, y);
8564
8565           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8566
8567           MovDelay[x][y] = 50;
8568
8569           /* !!! */
8570 #if 0
8571           RemoveField(newx, newy);
8572 #endif
8573           Feld[newx][newy] = EL_FLAMES;
8574           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8575           {
8576 #if 0
8577             RemoveField(newx1, newy1);
8578 #endif
8579             Feld[newx1][newy1] = EL_FLAMES;
8580           }
8581           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8582           {
8583 #if 0
8584             RemoveField(newx2, newy2);
8585 #endif
8586             Feld[newx2][newy2] = EL_FLAMES;
8587           }
8588
8589           return;
8590         }
8591       }
8592     }
8593     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8594              Feld[newx][newy] == EL_DIAMOND)
8595     {
8596       if (IS_MOVING(newx, newy))
8597         RemoveMovingField(newx, newy);
8598       else
8599       {
8600         Feld[newx][newy] = EL_EMPTY;
8601         TEST_DrawLevelField(newx, newy);
8602       }
8603
8604       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8605     }
8606     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8607              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8608     {
8609       if (AmoebaNr[newx][newy])
8610       {
8611         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8612         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8613             Feld[newx][newy] == EL_BD_AMOEBA)
8614           AmoebaCnt[AmoebaNr[newx][newy]]--;
8615       }
8616
8617 #if 0
8618       /* !!! test !!! */
8619       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8620       {
8621         RemoveMovingField(newx, newy);
8622       }
8623 #else
8624       if (IS_MOVING(newx, newy))
8625       {
8626         RemoveMovingField(newx, newy);
8627       }
8628 #endif
8629       else
8630       {
8631         Feld[newx][newy] = EL_EMPTY;
8632         TEST_DrawLevelField(newx, newy);
8633       }
8634
8635       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8636     }
8637     else if ((element == EL_PACMAN || element == EL_MOLE)
8638              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8639     {
8640       if (AmoebaNr[newx][newy])
8641       {
8642         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8643         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8644             Feld[newx][newy] == EL_BD_AMOEBA)
8645           AmoebaCnt[AmoebaNr[newx][newy]]--;
8646       }
8647
8648       if (element == EL_MOLE)
8649       {
8650         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8651         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8652
8653         ResetGfxAnimation(x, y);
8654         GfxAction[x][y] = ACTION_DIGGING;
8655         TEST_DrawLevelField(x, y);
8656
8657         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8658
8659         return;                         /* wait for shrinking amoeba */
8660       }
8661       else      /* element == EL_PACMAN */
8662       {
8663         Feld[newx][newy] = EL_EMPTY;
8664         TEST_DrawLevelField(newx, newy);
8665         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8666       }
8667     }
8668     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8669              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8670               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8671     {
8672       /* wait for shrinking amoeba to completely disappear */
8673       return;
8674     }
8675     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8676     {
8677       /* object was running against a wall */
8678
8679       TurnRound(x, y);
8680
8681 #if 0
8682       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8683       if (move_pattern & MV_ANY_DIRECTION &&
8684           move_pattern == MovDir[x][y])
8685       {
8686         int blocking_element =
8687           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8688
8689         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8690                                  MovDir[x][y]);
8691
8692         element = Feld[x][y];   /* element might have changed */
8693       }
8694 #endif
8695
8696       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8697         DrawLevelElementAnimation(x, y, element);
8698
8699       if (DONT_TOUCH(element))
8700         TestIfBadThingTouchesPlayer(x, y);
8701
8702       return;
8703     }
8704
8705     InitMovingField(x, y, MovDir[x][y]);
8706
8707     PlayLevelSoundAction(x, y, ACTION_MOVING);
8708   }
8709
8710   if (MovDir[x][y])
8711     ContinueMoving(x, y);
8712 }
8713
8714 void ContinueMoving(int x, int y)
8715 {
8716   int element = Feld[x][y];
8717   struct ElementInfo *ei = &element_info[element];
8718   int direction = MovDir[x][y];
8719   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8720   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8721   int newx = x + dx, newy = y + dy;
8722   int stored = Store[x][y];
8723   int stored_new = Store[newx][newy];
8724   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8725   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8726   boolean last_line = (newy == lev_fieldy - 1);
8727
8728   MovPos[x][y] += getElementMoveStepsize(x, y);
8729
8730   if (pushed_by_player) /* special case: moving object pushed by player */
8731     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8732
8733   if (ABS(MovPos[x][y]) < TILEX)
8734   {
8735 #if 0
8736     int ee = Feld[x][y];
8737     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8738     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8739
8740     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8741            x, y, ABS(MovPos[x][y]),
8742            ee, gg, ff,
8743            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8744 #endif
8745
8746     TEST_DrawLevelField(x, y);
8747
8748     return;     /* element is still moving */
8749   }
8750
8751   /* element reached destination field */
8752
8753   Feld[x][y] = EL_EMPTY;
8754   Feld[newx][newy] = element;
8755   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8756
8757   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8758   {
8759     element = Feld[newx][newy] = EL_ACID;
8760   }
8761   else if (element == EL_MOLE)
8762   {
8763     Feld[x][y] = EL_SAND;
8764
8765     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8766   }
8767   else if (element == EL_QUICKSAND_FILLING)
8768   {
8769     element = Feld[newx][newy] = get_next_element(element);
8770     Store[newx][newy] = Store[x][y];
8771   }
8772   else if (element == EL_QUICKSAND_EMPTYING)
8773   {
8774     Feld[x][y] = get_next_element(element);
8775     element = Feld[newx][newy] = Store[x][y];
8776   }
8777   else if (element == EL_QUICKSAND_FAST_FILLING)
8778   {
8779     element = Feld[newx][newy] = get_next_element(element);
8780     Store[newx][newy] = Store[x][y];
8781   }
8782   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8783   {
8784     Feld[x][y] = get_next_element(element);
8785     element = Feld[newx][newy] = Store[x][y];
8786   }
8787   else if (element == EL_MAGIC_WALL_FILLING)
8788   {
8789     element = Feld[newx][newy] = get_next_element(element);
8790     if (!game.magic_wall_active)
8791       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8792     Store[newx][newy] = Store[x][y];
8793   }
8794   else if (element == EL_MAGIC_WALL_EMPTYING)
8795   {
8796     Feld[x][y] = get_next_element(element);
8797     if (!game.magic_wall_active)
8798       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8799     element = Feld[newx][newy] = Store[x][y];
8800
8801 #if USE_NEW_CUSTOM_VALUE
8802     InitField(newx, newy, FALSE);
8803 #endif
8804   }
8805   else if (element == EL_BD_MAGIC_WALL_FILLING)
8806   {
8807     element = Feld[newx][newy] = get_next_element(element);
8808     if (!game.magic_wall_active)
8809       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8810     Store[newx][newy] = Store[x][y];
8811   }
8812   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8813   {
8814     Feld[x][y] = get_next_element(element);
8815     if (!game.magic_wall_active)
8816       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8817     element = Feld[newx][newy] = Store[x][y];
8818
8819 #if USE_NEW_CUSTOM_VALUE
8820     InitField(newx, newy, FALSE);
8821 #endif
8822   }
8823   else if (element == EL_DC_MAGIC_WALL_FILLING)
8824   {
8825     element = Feld[newx][newy] = get_next_element(element);
8826     if (!game.magic_wall_active)
8827       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8828     Store[newx][newy] = Store[x][y];
8829   }
8830   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8831   {
8832     Feld[x][y] = get_next_element(element);
8833     if (!game.magic_wall_active)
8834       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8835     element = Feld[newx][newy] = Store[x][y];
8836
8837 #if USE_NEW_CUSTOM_VALUE
8838     InitField(newx, newy, FALSE);
8839 #endif
8840   }
8841   else if (element == EL_AMOEBA_DROPPING)
8842   {
8843     Feld[x][y] = get_next_element(element);
8844     element = Feld[newx][newy] = Store[x][y];
8845   }
8846   else if (element == EL_SOKOBAN_OBJECT)
8847   {
8848     if (Back[x][y])
8849       Feld[x][y] = Back[x][y];
8850
8851     if (Back[newx][newy])
8852       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8853
8854     Back[x][y] = Back[newx][newy] = 0;
8855   }
8856
8857   Store[x][y] = EL_EMPTY;
8858   MovPos[x][y] = 0;
8859   MovDir[x][y] = 0;
8860   MovDelay[x][y] = 0;
8861
8862   MovDelay[newx][newy] = 0;
8863
8864   if (CAN_CHANGE_OR_HAS_ACTION(element))
8865   {
8866     /* copy element change control values to new field */
8867     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8868     ChangePage[newx][newy]  = ChangePage[x][y];
8869     ChangeCount[newx][newy] = ChangeCount[x][y];
8870     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8871   }
8872
8873 #if USE_NEW_CUSTOM_VALUE
8874   CustomValue[newx][newy] = CustomValue[x][y];
8875 #endif
8876
8877   ChangeDelay[x][y] = 0;
8878   ChangePage[x][y] = -1;
8879   ChangeCount[x][y] = 0;
8880   ChangeEvent[x][y] = -1;
8881
8882 #if USE_NEW_CUSTOM_VALUE
8883   CustomValue[x][y] = 0;
8884 #endif
8885
8886   /* copy animation control values to new field */
8887   GfxFrame[newx][newy]  = GfxFrame[x][y];
8888   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8889   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8890   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8891
8892   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8893
8894   /* some elements can leave other elements behind after moving */
8895 #if 1
8896   if (ei->move_leave_element != EL_EMPTY &&
8897       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8898       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8899 #else
8900   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8901       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8902       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8903 #endif
8904   {
8905     int move_leave_element = ei->move_leave_element;
8906
8907 #if 1
8908 #if 1
8909     /* this makes it possible to leave the removed element again */
8910     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8911       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8912 #else
8913     /* this makes it possible to leave the removed element again */
8914     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8915       move_leave_element = stored;
8916 #endif
8917 #else
8918     /* this makes it possible to leave the removed element again */
8919     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8920         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8921       move_leave_element = stored;
8922 #endif
8923
8924     Feld[x][y] = move_leave_element;
8925
8926     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8927       MovDir[x][y] = direction;
8928
8929     InitField(x, y, FALSE);
8930
8931     if (GFX_CRUMBLED(Feld[x][y]))
8932       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8933
8934     if (ELEM_IS_PLAYER(move_leave_element))
8935       RelocatePlayer(x, y, move_leave_element);
8936   }
8937
8938   /* do this after checking for left-behind element */
8939   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8940
8941   if (!CAN_MOVE(element) ||
8942       (CAN_FALL(element) && direction == MV_DOWN &&
8943        (element == EL_SPRING ||
8944         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8945         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8946     GfxDir[x][y] = MovDir[newx][newy] = 0;
8947
8948   TEST_DrawLevelField(x, y);
8949   TEST_DrawLevelField(newx, newy);
8950
8951   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8952
8953   /* prevent pushed element from moving on in pushed direction */
8954   if (pushed_by_player && CAN_MOVE(element) &&
8955       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8956       !(element_info[element].move_pattern & direction))
8957     TurnRound(newx, newy);
8958
8959   /* prevent elements on conveyor belt from moving on in last direction */
8960   if (pushed_by_conveyor && CAN_FALL(element) &&
8961       direction & MV_HORIZONTAL)
8962     MovDir[newx][newy] = 0;
8963
8964   if (!pushed_by_player)
8965   {
8966     int nextx = newx + dx, nexty = newy + dy;
8967     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8968
8969     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8970
8971     if (CAN_FALL(element) && direction == MV_DOWN)
8972       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8973
8974     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8975       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8976
8977 #if USE_FIX_IMPACT_COLLISION
8978     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8979       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8980 #endif
8981   }
8982
8983   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8984   {
8985     TestIfBadThingTouchesPlayer(newx, newy);
8986     TestIfBadThingTouchesFriend(newx, newy);
8987
8988     if (!IS_CUSTOM_ELEMENT(element))
8989       TestIfBadThingTouchesOtherBadThing(newx, newy);
8990   }
8991   else if (element == EL_PENGUIN)
8992     TestIfFriendTouchesBadThing(newx, newy);
8993
8994   if (DONT_GET_HIT_BY(element))
8995   {
8996     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8997   }
8998
8999   /* give the player one last chance (one more frame) to move away */
9000   if (CAN_FALL(element) && direction == MV_DOWN &&
9001       (last_line || (!IS_FREE(x, newy + 1) &&
9002                      (!IS_PLAYER(x, newy + 1) ||
9003                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9004     Impact(x, newy);
9005
9006   if (pushed_by_player && !game.use_change_when_pushing_bug)
9007   {
9008     int push_side = MV_DIR_OPPOSITE(direction);
9009     struct PlayerInfo *player = PLAYERINFO(x, y);
9010
9011     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9012                                player->index_bit, push_side);
9013     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9014                                         player->index_bit, push_side);
9015   }
9016
9017   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9018     MovDelay[newx][newy] = 1;
9019
9020   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9021
9022   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9023
9024 #if 0
9025   if (ChangePage[newx][newy] != -1)             /* delayed change */
9026   {
9027     int page = ChangePage[newx][newy];
9028     struct ElementChangeInfo *change = &ei->change_page[page];
9029
9030     ChangePage[newx][newy] = -1;
9031
9032     if (change->can_change)
9033     {
9034       if (ChangeElement(newx, newy, element, page))
9035       {
9036         if (change->post_change_function)
9037           change->post_change_function(newx, newy);
9038       }
9039     }
9040
9041     if (change->has_action)
9042       ExecuteCustomElementAction(newx, newy, element, page);
9043   }
9044 #endif
9045
9046   TestIfElementHitsCustomElement(newx, newy, direction);
9047   TestIfPlayerTouchesCustomElement(newx, newy);
9048   TestIfElementTouchesCustomElement(newx, newy);
9049
9050   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9051       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9052     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9053                              MV_DIR_OPPOSITE(direction));
9054 }
9055
9056 int AmoebeNachbarNr(int ax, int ay)
9057 {
9058   int i;
9059   int element = Feld[ax][ay];
9060   int group_nr = 0;
9061   static int xy[4][2] =
9062   {
9063     { 0, -1 },
9064     { -1, 0 },
9065     { +1, 0 },
9066     { 0, +1 }
9067   };
9068
9069   for (i = 0; i < NUM_DIRECTIONS; i++)
9070   {
9071     int x = ax + xy[i][0];
9072     int y = ay + xy[i][1];
9073
9074     if (!IN_LEV_FIELD(x, y))
9075       continue;
9076
9077     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9078       group_nr = AmoebaNr[x][y];
9079   }
9080
9081   return group_nr;
9082 }
9083
9084 void AmoebenVereinigen(int ax, int ay)
9085 {
9086   int i, x, y, xx, yy;
9087   int new_group_nr = AmoebaNr[ax][ay];
9088   static int xy[4][2] =
9089   {
9090     { 0, -1 },
9091     { -1, 0 },
9092     { +1, 0 },
9093     { 0, +1 }
9094   };
9095
9096   if (new_group_nr == 0)
9097     return;
9098
9099   for (i = 0; i < NUM_DIRECTIONS; i++)
9100   {
9101     x = ax + xy[i][0];
9102     y = ay + xy[i][1];
9103
9104     if (!IN_LEV_FIELD(x, y))
9105       continue;
9106
9107     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9108          Feld[x][y] == EL_BD_AMOEBA ||
9109          Feld[x][y] == EL_AMOEBA_DEAD) &&
9110         AmoebaNr[x][y] != new_group_nr)
9111     {
9112       int old_group_nr = AmoebaNr[x][y];
9113
9114       if (old_group_nr == 0)
9115         return;
9116
9117       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9118       AmoebaCnt[old_group_nr] = 0;
9119       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9120       AmoebaCnt2[old_group_nr] = 0;
9121
9122       SCAN_PLAYFIELD(xx, yy)
9123       {
9124         if (AmoebaNr[xx][yy] == old_group_nr)
9125           AmoebaNr[xx][yy] = new_group_nr;
9126       }
9127     }
9128   }
9129 }
9130
9131 void AmoebeUmwandeln(int ax, int ay)
9132 {
9133   int i, x, y;
9134
9135   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9136   {
9137     int group_nr = AmoebaNr[ax][ay];
9138
9139 #ifdef DEBUG
9140     if (group_nr == 0)
9141     {
9142       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9143       printf("AmoebeUmwandeln(): This should never happen!\n");
9144       return;
9145     }
9146 #endif
9147
9148     SCAN_PLAYFIELD(x, y)
9149     {
9150       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9151       {
9152         AmoebaNr[x][y] = 0;
9153         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9154       }
9155     }
9156
9157     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9158                             SND_AMOEBA_TURNING_TO_GEM :
9159                             SND_AMOEBA_TURNING_TO_ROCK));
9160     Bang(ax, ay);
9161   }
9162   else
9163   {
9164     static int xy[4][2] =
9165     {
9166       { 0, -1 },
9167       { -1, 0 },
9168       { +1, 0 },
9169       { 0, +1 }
9170     };
9171
9172     for (i = 0; i < NUM_DIRECTIONS; i++)
9173     {
9174       x = ax + xy[i][0];
9175       y = ay + xy[i][1];
9176
9177       if (!IN_LEV_FIELD(x, y))
9178         continue;
9179
9180       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9181       {
9182         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9183                               SND_AMOEBA_TURNING_TO_GEM :
9184                               SND_AMOEBA_TURNING_TO_ROCK));
9185         Bang(x, y);
9186       }
9187     }
9188   }
9189 }
9190
9191 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9192 {
9193   int x, y;
9194   int group_nr = AmoebaNr[ax][ay];
9195   boolean done = FALSE;
9196
9197 #ifdef DEBUG
9198   if (group_nr == 0)
9199   {
9200     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9201     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9202     return;
9203   }
9204 #endif
9205
9206   SCAN_PLAYFIELD(x, y)
9207   {
9208     if (AmoebaNr[x][y] == group_nr &&
9209         (Feld[x][y] == EL_AMOEBA_DEAD ||
9210          Feld[x][y] == EL_BD_AMOEBA ||
9211          Feld[x][y] == EL_AMOEBA_GROWING))
9212     {
9213       AmoebaNr[x][y] = 0;
9214       Feld[x][y] = new_element;
9215       InitField(x, y, FALSE);
9216       TEST_DrawLevelField(x, y);
9217       done = TRUE;
9218     }
9219   }
9220
9221   if (done)
9222     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9223                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9224                             SND_BD_AMOEBA_TURNING_TO_GEM));
9225 }
9226
9227 void AmoebeWaechst(int x, int y)
9228 {
9229   static unsigned long sound_delay = 0;
9230   static unsigned long sound_delay_value = 0;
9231
9232   if (!MovDelay[x][y])          /* start new growing cycle */
9233   {
9234     MovDelay[x][y] = 7;
9235
9236     if (DelayReached(&sound_delay, sound_delay_value))
9237     {
9238       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9239       sound_delay_value = 30;
9240     }
9241   }
9242
9243   if (MovDelay[x][y])           /* wait some time before growing bigger */
9244   {
9245     MovDelay[x][y]--;
9246     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9247     {
9248       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9249                                            6 - MovDelay[x][y]);
9250
9251       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9252     }
9253
9254     if (!MovDelay[x][y])
9255     {
9256       Feld[x][y] = Store[x][y];
9257       Store[x][y] = 0;
9258       TEST_DrawLevelField(x, y);
9259     }
9260   }
9261 }
9262
9263 void AmoebaDisappearing(int x, int y)
9264 {
9265   static unsigned long sound_delay = 0;
9266   static unsigned long sound_delay_value = 0;
9267
9268   if (!MovDelay[x][y])          /* start new shrinking cycle */
9269   {
9270     MovDelay[x][y] = 7;
9271
9272     if (DelayReached(&sound_delay, sound_delay_value))
9273       sound_delay_value = 30;
9274   }
9275
9276   if (MovDelay[x][y])           /* wait some time before shrinking */
9277   {
9278     MovDelay[x][y]--;
9279     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9280     {
9281       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9282                                            6 - MovDelay[x][y]);
9283
9284       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9285     }
9286
9287     if (!MovDelay[x][y])
9288     {
9289       Feld[x][y] = EL_EMPTY;
9290       TEST_DrawLevelField(x, y);
9291
9292       /* don't let mole enter this field in this cycle;
9293          (give priority to objects falling to this field from above) */
9294       Stop[x][y] = TRUE;
9295     }
9296   }
9297 }
9298
9299 void AmoebeAbleger(int ax, int ay)
9300 {
9301   int i;
9302   int element = Feld[ax][ay];
9303   int graphic = el2img(element);
9304   int newax = ax, neway = ay;
9305   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9306   static int xy[4][2] =
9307   {
9308     { 0, -1 },
9309     { -1, 0 },
9310     { +1, 0 },
9311     { 0, +1 }
9312   };
9313
9314   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9315   {
9316     Feld[ax][ay] = EL_AMOEBA_DEAD;
9317     TEST_DrawLevelField(ax, ay);
9318     return;
9319   }
9320
9321   if (IS_ANIMATED(graphic))
9322     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9323
9324   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9325     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9326
9327   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9328   {
9329     MovDelay[ax][ay]--;
9330     if (MovDelay[ax][ay])
9331       return;
9332   }
9333
9334   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9335   {
9336     int start = RND(4);
9337     int x = ax + xy[start][0];
9338     int y = ay + xy[start][1];
9339
9340     if (!IN_LEV_FIELD(x, y))
9341       return;
9342
9343     if (IS_FREE(x, y) ||
9344         CAN_GROW_INTO(Feld[x][y]) ||
9345         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9346         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9347     {
9348       newax = x;
9349       neway = y;
9350     }
9351
9352     if (newax == ax && neway == ay)
9353       return;
9354   }
9355   else                          /* normal or "filled" (BD style) amoeba */
9356   {
9357     int start = RND(4);
9358     boolean waiting_for_player = FALSE;
9359
9360     for (i = 0; i < NUM_DIRECTIONS; i++)
9361     {
9362       int j = (start + i) % 4;
9363       int x = ax + xy[j][0];
9364       int y = ay + xy[j][1];
9365
9366       if (!IN_LEV_FIELD(x, y))
9367         continue;
9368
9369       if (IS_FREE(x, y) ||
9370           CAN_GROW_INTO(Feld[x][y]) ||
9371           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9372           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9373       {
9374         newax = x;
9375         neway = y;
9376         break;
9377       }
9378       else if (IS_PLAYER(x, y))
9379         waiting_for_player = TRUE;
9380     }
9381
9382     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9383     {
9384       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9385       {
9386         Feld[ax][ay] = EL_AMOEBA_DEAD;
9387         TEST_DrawLevelField(ax, ay);
9388         AmoebaCnt[AmoebaNr[ax][ay]]--;
9389
9390         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9391         {
9392           if (element == EL_AMOEBA_FULL)
9393             AmoebeUmwandeln(ax, ay);
9394           else if (element == EL_BD_AMOEBA)
9395             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9396         }
9397       }
9398       return;
9399     }
9400     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9401     {
9402       /* amoeba gets larger by growing in some direction */
9403
9404       int new_group_nr = AmoebaNr[ax][ay];
9405
9406 #ifdef DEBUG
9407   if (new_group_nr == 0)
9408   {
9409     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9410     printf("AmoebeAbleger(): This should never happen!\n");
9411     return;
9412   }
9413 #endif
9414
9415       AmoebaNr[newax][neway] = new_group_nr;
9416       AmoebaCnt[new_group_nr]++;
9417       AmoebaCnt2[new_group_nr]++;
9418
9419       /* if amoeba touches other amoeba(s) after growing, unify them */
9420       AmoebenVereinigen(newax, neway);
9421
9422       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9423       {
9424         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9425         return;
9426       }
9427     }
9428   }
9429
9430   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9431       (neway == lev_fieldy - 1 && newax != ax))
9432   {
9433     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9434     Store[newax][neway] = element;
9435   }
9436   else if (neway == ay || element == EL_EMC_DRIPPER)
9437   {
9438     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9439
9440     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9441   }
9442   else
9443   {
9444     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9445     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9446     Store[ax][ay] = EL_AMOEBA_DROP;
9447     ContinueMoving(ax, ay);
9448     return;
9449   }
9450
9451   TEST_DrawLevelField(newax, neway);
9452 }
9453
9454 void Life(int ax, int ay)
9455 {
9456   int x1, y1, x2, y2;
9457   int life_time = 40;
9458   int element = Feld[ax][ay];
9459   int graphic = el2img(element);
9460   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9461                          level.biomaze);
9462   boolean changed = FALSE;
9463
9464   if (IS_ANIMATED(graphic))
9465     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9466
9467   if (Stop[ax][ay])
9468     return;
9469
9470   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9471     MovDelay[ax][ay] = life_time;
9472
9473   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9474   {
9475     MovDelay[ax][ay]--;
9476     if (MovDelay[ax][ay])
9477       return;
9478   }
9479
9480   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9481   {
9482     int xx = ax+x1, yy = ay+y1;
9483     int nachbarn = 0;
9484
9485     if (!IN_LEV_FIELD(xx, yy))
9486       continue;
9487
9488     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9489     {
9490       int x = xx+x2, y = yy+y2;
9491
9492       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9493         continue;
9494
9495       if (((Feld[x][y] == element ||
9496             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9497            !Stop[x][y]) ||
9498           (IS_FREE(x, y) && Stop[x][y]))
9499         nachbarn++;
9500     }
9501
9502     if (xx == ax && yy == ay)           /* field in the middle */
9503     {
9504       if (nachbarn < life_parameter[0] ||
9505           nachbarn > life_parameter[1])
9506       {
9507         Feld[xx][yy] = EL_EMPTY;
9508         if (!Stop[xx][yy])
9509           TEST_DrawLevelField(xx, yy);
9510         Stop[xx][yy] = TRUE;
9511         changed = TRUE;
9512       }
9513     }
9514     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9515     {                                   /* free border field */
9516       if (nachbarn >= life_parameter[2] &&
9517           nachbarn <= life_parameter[3])
9518       {
9519         Feld[xx][yy] = element;
9520         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9521         if (!Stop[xx][yy])
9522           TEST_DrawLevelField(xx, yy);
9523         Stop[xx][yy] = TRUE;
9524         changed = TRUE;
9525       }
9526     }
9527   }
9528
9529   if (changed)
9530     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9531                    SND_GAME_OF_LIFE_GROWING);
9532 }
9533
9534 static void InitRobotWheel(int x, int y)
9535 {
9536   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9537 }
9538
9539 static void RunRobotWheel(int x, int y)
9540 {
9541   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9542 }
9543
9544 static void StopRobotWheel(int x, int y)
9545 {
9546   if (ZX == x && ZY == y)
9547   {
9548     ZX = ZY = -1;
9549
9550     game.robot_wheel_active = FALSE;
9551   }
9552 }
9553
9554 static void InitTimegateWheel(int x, int y)
9555 {
9556   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9557 }
9558
9559 static void RunTimegateWheel(int x, int y)
9560 {
9561   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9562 }
9563
9564 static void InitMagicBallDelay(int x, int y)
9565 {
9566 #if 1
9567   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9568 #else
9569   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9570 #endif
9571 }
9572
9573 static void ActivateMagicBall(int bx, int by)
9574 {
9575   int x, y;
9576
9577   if (level.ball_random)
9578   {
9579     int pos_border = RND(8);    /* select one of the eight border elements */
9580     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9581     int xx = pos_content % 3;
9582     int yy = pos_content / 3;
9583
9584     x = bx - 1 + xx;
9585     y = by - 1 + yy;
9586
9587     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9588       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9589   }
9590   else
9591   {
9592     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9593     {
9594       int xx = x - bx + 1;
9595       int yy = y - by + 1;
9596
9597       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9598         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9599     }
9600   }
9601
9602   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9603 }
9604
9605 void CheckExit(int x, int y)
9606 {
9607   if (local_player->gems_still_needed > 0 ||
9608       local_player->sokobanfields_still_needed > 0 ||
9609       local_player->lights_still_needed > 0)
9610   {
9611     int element = Feld[x][y];
9612     int graphic = el2img(element);
9613
9614     if (IS_ANIMATED(graphic))
9615       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9616
9617     return;
9618   }
9619
9620   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9621     return;
9622
9623   Feld[x][y] = EL_EXIT_OPENING;
9624
9625   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9626 }
9627
9628 void CheckExitEM(int x, int y)
9629 {
9630   if (local_player->gems_still_needed > 0 ||
9631       local_player->sokobanfields_still_needed > 0 ||
9632       local_player->lights_still_needed > 0)
9633   {
9634     int element = Feld[x][y];
9635     int graphic = el2img(element);
9636
9637     if (IS_ANIMATED(graphic))
9638       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9639
9640     return;
9641   }
9642
9643   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9644     return;
9645
9646   Feld[x][y] = EL_EM_EXIT_OPENING;
9647
9648   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9649 }
9650
9651 void CheckExitSteel(int x, int y)
9652 {
9653   if (local_player->gems_still_needed > 0 ||
9654       local_player->sokobanfields_still_needed > 0 ||
9655       local_player->lights_still_needed > 0)
9656   {
9657     int element = Feld[x][y];
9658     int graphic = el2img(element);
9659
9660     if (IS_ANIMATED(graphic))
9661       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9662
9663     return;
9664   }
9665
9666   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9667     return;
9668
9669   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9670
9671   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9672 }
9673
9674 void CheckExitSteelEM(int x, int y)
9675 {
9676   if (local_player->gems_still_needed > 0 ||
9677       local_player->sokobanfields_still_needed > 0 ||
9678       local_player->lights_still_needed > 0)
9679   {
9680     int element = Feld[x][y];
9681     int graphic = el2img(element);
9682
9683     if (IS_ANIMATED(graphic))
9684       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9685
9686     return;
9687   }
9688
9689   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9690     return;
9691
9692   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9693
9694   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9695 }
9696
9697 void CheckExitSP(int x, int y)
9698 {
9699   if (local_player->gems_still_needed > 0)
9700   {
9701     int element = Feld[x][y];
9702     int graphic = el2img(element);
9703
9704     if (IS_ANIMATED(graphic))
9705       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9706
9707     return;
9708   }
9709
9710   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9711     return;
9712
9713   Feld[x][y] = EL_SP_EXIT_OPENING;
9714
9715   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9716 }
9717
9718 static void CloseAllOpenTimegates()
9719 {
9720   int x, y;
9721
9722   SCAN_PLAYFIELD(x, y)
9723   {
9724     int element = Feld[x][y];
9725
9726     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9727     {
9728       Feld[x][y] = EL_TIMEGATE_CLOSING;
9729
9730       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9731     }
9732   }
9733 }
9734
9735 void DrawTwinkleOnField(int x, int y)
9736 {
9737   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9738     return;
9739
9740   if (Feld[x][y] == EL_BD_DIAMOND)
9741     return;
9742
9743   if (MovDelay[x][y] == 0)      /* next animation frame */
9744     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9745
9746   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9747   {
9748     MovDelay[x][y]--;
9749
9750     DrawLevelElementAnimation(x, y, Feld[x][y]);
9751
9752     if (MovDelay[x][y] != 0)
9753     {
9754       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9755                                            10 - MovDelay[x][y]);
9756
9757       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9758     }
9759   }
9760 }
9761
9762 void MauerWaechst(int x, int y)
9763 {
9764   int delay = 6;
9765
9766   if (!MovDelay[x][y])          /* next animation frame */
9767     MovDelay[x][y] = 3 * delay;
9768
9769   if (MovDelay[x][y])           /* wait some time before next frame */
9770   {
9771     MovDelay[x][y]--;
9772
9773     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9774     {
9775       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9776       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9777
9778       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9779     }
9780
9781     if (!MovDelay[x][y])
9782     {
9783       if (MovDir[x][y] == MV_LEFT)
9784       {
9785         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9786           TEST_DrawLevelField(x - 1, y);
9787       }
9788       else if (MovDir[x][y] == MV_RIGHT)
9789       {
9790         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9791           TEST_DrawLevelField(x + 1, y);
9792       }
9793       else if (MovDir[x][y] == MV_UP)
9794       {
9795         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9796           TEST_DrawLevelField(x, y - 1);
9797       }
9798       else
9799       {
9800         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9801           TEST_DrawLevelField(x, y + 1);
9802       }
9803
9804       Feld[x][y] = Store[x][y];
9805       Store[x][y] = 0;
9806       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9807       TEST_DrawLevelField(x, y);
9808     }
9809   }
9810 }
9811
9812 void MauerAbleger(int ax, int ay)
9813 {
9814   int element = Feld[ax][ay];
9815   int graphic = el2img(element);
9816   boolean oben_frei = FALSE, unten_frei = FALSE;
9817   boolean links_frei = FALSE, rechts_frei = FALSE;
9818   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9819   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9820   boolean new_wall = FALSE;
9821
9822   if (IS_ANIMATED(graphic))
9823     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9824
9825   if (!MovDelay[ax][ay])        /* start building new wall */
9826     MovDelay[ax][ay] = 6;
9827
9828   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9829   {
9830     MovDelay[ax][ay]--;
9831     if (MovDelay[ax][ay])
9832       return;
9833   }
9834
9835   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9836     oben_frei = TRUE;
9837   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9838     unten_frei = TRUE;
9839   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9840     links_frei = TRUE;
9841   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9842     rechts_frei = TRUE;
9843
9844   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9845       element == EL_EXPANDABLE_WALL_ANY)
9846   {
9847     if (oben_frei)
9848     {
9849       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9850       Store[ax][ay-1] = element;
9851       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9852       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9853         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9854                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9855       new_wall = TRUE;
9856     }
9857     if (unten_frei)
9858     {
9859       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9860       Store[ax][ay+1] = element;
9861       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9862       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9863         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9864                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9865       new_wall = TRUE;
9866     }
9867   }
9868
9869   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9870       element == EL_EXPANDABLE_WALL_ANY ||
9871       element == EL_EXPANDABLE_WALL ||
9872       element == EL_BD_EXPANDABLE_WALL)
9873   {
9874     if (links_frei)
9875     {
9876       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9877       Store[ax-1][ay] = element;
9878       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9879       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9880         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9881                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9882       new_wall = TRUE;
9883     }
9884
9885     if (rechts_frei)
9886     {
9887       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9888       Store[ax+1][ay] = element;
9889       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9890       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9891         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9892                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9893       new_wall = TRUE;
9894     }
9895   }
9896
9897   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9898     TEST_DrawLevelField(ax, ay);
9899
9900   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9901     oben_massiv = TRUE;
9902   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9903     unten_massiv = TRUE;
9904   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9905     links_massiv = TRUE;
9906   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9907     rechts_massiv = TRUE;
9908
9909   if (((oben_massiv && unten_massiv) ||
9910        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9911        element == EL_EXPANDABLE_WALL) &&
9912       ((links_massiv && rechts_massiv) ||
9913        element == EL_EXPANDABLE_WALL_VERTICAL))
9914     Feld[ax][ay] = EL_WALL;
9915
9916   if (new_wall)
9917     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9918 }
9919
9920 void MauerAblegerStahl(int ax, int ay)
9921 {
9922   int element = Feld[ax][ay];
9923   int graphic = el2img(element);
9924   boolean oben_frei = FALSE, unten_frei = FALSE;
9925   boolean links_frei = FALSE, rechts_frei = FALSE;
9926   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9927   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9928   boolean new_wall = FALSE;
9929
9930   if (IS_ANIMATED(graphic))
9931     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9932
9933   if (!MovDelay[ax][ay])        /* start building new wall */
9934     MovDelay[ax][ay] = 6;
9935
9936   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9937   {
9938     MovDelay[ax][ay]--;
9939     if (MovDelay[ax][ay])
9940       return;
9941   }
9942
9943   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9944     oben_frei = TRUE;
9945   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9946     unten_frei = TRUE;
9947   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9948     links_frei = TRUE;
9949   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9950     rechts_frei = TRUE;
9951
9952   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9953       element == EL_EXPANDABLE_STEELWALL_ANY)
9954   {
9955     if (oben_frei)
9956     {
9957       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9958       Store[ax][ay-1] = element;
9959       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9960       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9961         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9962                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9963       new_wall = TRUE;
9964     }
9965     if (unten_frei)
9966     {
9967       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9968       Store[ax][ay+1] = element;
9969       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9970       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9971         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9972                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9973       new_wall = TRUE;
9974     }
9975   }
9976
9977   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9978       element == EL_EXPANDABLE_STEELWALL_ANY)
9979   {
9980     if (links_frei)
9981     {
9982       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9983       Store[ax-1][ay] = element;
9984       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9985       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9986         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9987                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9988       new_wall = TRUE;
9989     }
9990
9991     if (rechts_frei)
9992     {
9993       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9994       Store[ax+1][ay] = element;
9995       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9996       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9997         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9998                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9999       new_wall = TRUE;
10000     }
10001   }
10002
10003   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10004     oben_massiv = TRUE;
10005   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10006     unten_massiv = TRUE;
10007   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10008     links_massiv = TRUE;
10009   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10010     rechts_massiv = TRUE;
10011
10012   if (((oben_massiv && unten_massiv) ||
10013        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10014       ((links_massiv && rechts_massiv) ||
10015        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10016     Feld[ax][ay] = EL_STEELWALL;
10017
10018   if (new_wall)
10019     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10020 }
10021
10022 void CheckForDragon(int x, int y)
10023 {
10024   int i, j;
10025   boolean dragon_found = FALSE;
10026   static int xy[4][2] =
10027   {
10028     { 0, -1 },
10029     { -1, 0 },
10030     { +1, 0 },
10031     { 0, +1 }
10032   };
10033
10034   for (i = 0; i < NUM_DIRECTIONS; i++)
10035   {
10036     for (j = 0; j < 4; j++)
10037     {
10038       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10039
10040       if (IN_LEV_FIELD(xx, yy) &&
10041           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10042       {
10043         if (Feld[xx][yy] == EL_DRAGON)
10044           dragon_found = TRUE;
10045       }
10046       else
10047         break;
10048     }
10049   }
10050
10051   if (!dragon_found)
10052   {
10053     for (i = 0; i < NUM_DIRECTIONS; i++)
10054     {
10055       for (j = 0; j < 3; j++)
10056       {
10057         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10058   
10059         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10060         {
10061           Feld[xx][yy] = EL_EMPTY;
10062           TEST_DrawLevelField(xx, yy);
10063         }
10064         else
10065           break;
10066       }
10067     }
10068   }
10069 }
10070
10071 static void InitBuggyBase(int x, int y)
10072 {
10073   int element = Feld[x][y];
10074   int activating_delay = FRAMES_PER_SECOND / 4;
10075
10076   ChangeDelay[x][y] =
10077     (element == EL_SP_BUGGY_BASE ?
10078      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10079      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10080      activating_delay :
10081      element == EL_SP_BUGGY_BASE_ACTIVE ?
10082      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10083 }
10084
10085 static void WarnBuggyBase(int x, int y)
10086 {
10087   int i;
10088   static int xy[4][2] =
10089   {
10090     { 0, -1 },
10091     { -1, 0 },
10092     { +1, 0 },
10093     { 0, +1 }
10094   };
10095
10096   for (i = 0; i < NUM_DIRECTIONS; i++)
10097   {
10098     int xx = x + xy[i][0];
10099     int yy = y + xy[i][1];
10100
10101     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10102     {
10103       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10104
10105       break;
10106     }
10107   }
10108 }
10109
10110 static void InitTrap(int x, int y)
10111 {
10112   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10113 }
10114
10115 static void ActivateTrap(int x, int y)
10116 {
10117   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10118 }
10119
10120 static void ChangeActiveTrap(int x, int y)
10121 {
10122   int graphic = IMG_TRAP_ACTIVE;
10123
10124   /* if new animation frame was drawn, correct crumbled sand border */
10125   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10126     TEST_DrawLevelFieldCrumbledSand(x, y);
10127 }
10128
10129 static int getSpecialActionElement(int element, int number, int base_element)
10130 {
10131   return (element != EL_EMPTY ? element :
10132           number != -1 ? base_element + number - 1 :
10133           EL_EMPTY);
10134 }
10135
10136 static int getModifiedActionNumber(int value_old, int operator, int operand,
10137                                    int value_min, int value_max)
10138 {
10139   int value_new = (operator == CA_MODE_SET      ? operand :
10140                    operator == CA_MODE_ADD      ? value_old + operand :
10141                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10142                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10143                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10144                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10145                    value_old);
10146
10147   return (value_new < value_min ? value_min :
10148           value_new > value_max ? value_max :
10149           value_new);
10150 }
10151
10152 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10153 {
10154   struct ElementInfo *ei = &element_info[element];
10155   struct ElementChangeInfo *change = &ei->change_page[page];
10156   int target_element = change->target_element;
10157   int action_type = change->action_type;
10158   int action_mode = change->action_mode;
10159   int action_arg = change->action_arg;
10160   int action_element = change->action_element;
10161   int i;
10162
10163   if (!change->has_action)
10164     return;
10165
10166   /* ---------- determine action paramater values -------------------------- */
10167
10168   int level_time_value =
10169     (level.time > 0 ? TimeLeft :
10170      TimePlayed);
10171
10172   int action_arg_element_raw =
10173     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10174      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10175      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10176      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10177      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10178      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10179      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10180      EL_EMPTY);
10181   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10182
10183 #if 0
10184   if (action_arg_element_raw == EL_GROUP_START)
10185     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10186 #endif
10187
10188   int action_arg_direction =
10189     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10190      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10191      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10192      change->actual_trigger_side :
10193      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10194      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10195      MV_NONE);
10196
10197   int action_arg_number_min =
10198     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10199      CA_ARG_MIN);
10200
10201   int action_arg_number_max =
10202     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10203      action_type == CA_SET_LEVEL_GEMS ? 999 :
10204      action_type == CA_SET_LEVEL_TIME ? 9999 :
10205      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10206      action_type == CA_SET_CE_VALUE ? 9999 :
10207      action_type == CA_SET_CE_SCORE ? 9999 :
10208      CA_ARG_MAX);
10209
10210   int action_arg_number_reset =
10211     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10212      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10213      action_type == CA_SET_LEVEL_TIME ? level.time :
10214      action_type == CA_SET_LEVEL_SCORE ? 0 :
10215 #if USE_NEW_CUSTOM_VALUE
10216      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10217 #else
10218      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10219 #endif
10220      action_type == CA_SET_CE_SCORE ? 0 :
10221      0);
10222
10223   int action_arg_number =
10224     (action_arg <= CA_ARG_MAX ? action_arg :
10225      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10226      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10227      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10228      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10229      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10230      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10231 #if USE_NEW_CUSTOM_VALUE
10232      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10233 #else
10234      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10235 #endif
10236      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10237      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10238      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10239      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10240      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10241      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10242      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10243      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10244      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10245      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10246      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10247      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10248      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10249      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10250      -1);
10251
10252   int action_arg_number_old =
10253     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10254      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10255      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10256      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10257      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10258      0);
10259
10260   int action_arg_number_new =
10261     getModifiedActionNumber(action_arg_number_old,
10262                             action_mode, action_arg_number,
10263                             action_arg_number_min, action_arg_number_max);
10264
10265 #if 1
10266   int trigger_player_bits =
10267     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10268      change->actual_trigger_player_bits : change->trigger_player);
10269 #else
10270   int trigger_player_bits =
10271     (change->actual_trigger_player >= EL_PLAYER_1 &&
10272      change->actual_trigger_player <= EL_PLAYER_4 ?
10273      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10274      PLAYER_BITS_ANY);
10275 #endif
10276
10277   int action_arg_player_bits =
10278     (action_arg >= CA_ARG_PLAYER_1 &&
10279      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10280      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10281      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10282      PLAYER_BITS_ANY);
10283
10284   /* ---------- execute action  -------------------------------------------- */
10285
10286   switch (action_type)
10287   {
10288     case CA_NO_ACTION:
10289     {
10290       return;
10291     }
10292
10293     /* ---------- level actions  ------------------------------------------- */
10294
10295     case CA_RESTART_LEVEL:
10296     {
10297       game.restart_level = TRUE;
10298
10299       break;
10300     }
10301
10302     case CA_SHOW_ENVELOPE:
10303     {
10304       int element = getSpecialActionElement(action_arg_element,
10305                                             action_arg_number, EL_ENVELOPE_1);
10306
10307       if (IS_ENVELOPE(element))
10308         local_player->show_envelope = element;
10309
10310       break;
10311     }
10312
10313     case CA_SET_LEVEL_TIME:
10314     {
10315       if (level.time > 0)       /* only modify limited time value */
10316       {
10317         TimeLeft = action_arg_number_new;
10318
10319 #if 1
10320         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10321
10322         DisplayGameControlValues();
10323 #else
10324         DrawGameValue_Time(TimeLeft);
10325 #endif
10326
10327         if (!TimeLeft && setup.time_limit)
10328           for (i = 0; i < MAX_PLAYERS; i++)
10329             KillPlayer(&stored_player[i]);
10330       }
10331
10332       break;
10333     }
10334
10335     case CA_SET_LEVEL_SCORE:
10336     {
10337       local_player->score = action_arg_number_new;
10338
10339 #if 1
10340       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10341
10342       DisplayGameControlValues();
10343 #else
10344       DrawGameValue_Score(local_player->score);
10345 #endif
10346
10347       break;
10348     }
10349
10350     case CA_SET_LEVEL_GEMS:
10351     {
10352       local_player->gems_still_needed = action_arg_number_new;
10353
10354 #if 1
10355       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10356
10357       DisplayGameControlValues();
10358 #else
10359       DrawGameValue_Emeralds(local_player->gems_still_needed);
10360 #endif
10361
10362       break;
10363     }
10364
10365 #if !USE_PLAYER_GRAVITY
10366     case CA_SET_LEVEL_GRAVITY:
10367     {
10368       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10369                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10370                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10371                       game.gravity);
10372       break;
10373     }
10374 #endif
10375
10376     case CA_SET_LEVEL_WIND:
10377     {
10378       game.wind_direction = action_arg_direction;
10379
10380       break;
10381     }
10382
10383     /* ---------- player actions  ------------------------------------------ */
10384
10385     case CA_MOVE_PLAYER:
10386     {
10387       /* automatically move to the next field in specified direction */
10388       for (i = 0; i < MAX_PLAYERS; i++)
10389         if (trigger_player_bits & (1 << i))
10390           stored_player[i].programmed_action = action_arg_direction;
10391
10392       break;
10393     }
10394
10395     case CA_EXIT_PLAYER:
10396     {
10397       for (i = 0; i < MAX_PLAYERS; i++)
10398         if (action_arg_player_bits & (1 << i))
10399           PlayerWins(&stored_player[i]);
10400
10401       break;
10402     }
10403
10404     case CA_KILL_PLAYER:
10405     {
10406       for (i = 0; i < MAX_PLAYERS; i++)
10407         if (action_arg_player_bits & (1 << i))
10408           KillPlayer(&stored_player[i]);
10409
10410       break;
10411     }
10412
10413     case CA_SET_PLAYER_KEYS:
10414     {
10415       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10416       int element = getSpecialActionElement(action_arg_element,
10417                                             action_arg_number, EL_KEY_1);
10418
10419       if (IS_KEY(element))
10420       {
10421         for (i = 0; i < MAX_PLAYERS; i++)
10422         {
10423           if (trigger_player_bits & (1 << i))
10424           {
10425             stored_player[i].key[KEY_NR(element)] = key_state;
10426
10427             DrawGameDoorValues();
10428           }
10429         }
10430       }
10431
10432       break;
10433     }
10434
10435     case CA_SET_PLAYER_SPEED:
10436     {
10437 #if 0
10438       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10439 #endif
10440
10441       for (i = 0; i < MAX_PLAYERS; i++)
10442       {
10443         if (trigger_player_bits & (1 << i))
10444         {
10445           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10446
10447           if (action_arg == CA_ARG_SPEED_FASTER &&
10448               stored_player[i].cannot_move)
10449           {
10450             action_arg_number = STEPSIZE_VERY_SLOW;
10451           }
10452           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10453                    action_arg == CA_ARG_SPEED_FASTER)
10454           {
10455             action_arg_number = 2;
10456             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10457                            CA_MODE_MULTIPLY);
10458           }
10459           else if (action_arg == CA_ARG_NUMBER_RESET)
10460           {
10461             action_arg_number = level.initial_player_stepsize[i];
10462           }
10463
10464           move_stepsize =
10465             getModifiedActionNumber(move_stepsize,
10466                                     action_mode,
10467                                     action_arg_number,
10468                                     action_arg_number_min,
10469                                     action_arg_number_max);
10470
10471           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10472         }
10473       }
10474
10475       break;
10476     }
10477
10478     case CA_SET_PLAYER_SHIELD:
10479     {
10480       for (i = 0; i < MAX_PLAYERS; i++)
10481       {
10482         if (trigger_player_bits & (1 << i))
10483         {
10484           if (action_arg == CA_ARG_SHIELD_OFF)
10485           {
10486             stored_player[i].shield_normal_time_left = 0;
10487             stored_player[i].shield_deadly_time_left = 0;
10488           }
10489           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10490           {
10491             stored_player[i].shield_normal_time_left = 999999;
10492           }
10493           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10494           {
10495             stored_player[i].shield_normal_time_left = 999999;
10496             stored_player[i].shield_deadly_time_left = 999999;
10497           }
10498         }
10499       }
10500
10501       break;
10502     }
10503
10504 #if USE_PLAYER_GRAVITY
10505     case CA_SET_PLAYER_GRAVITY:
10506     {
10507       for (i = 0; i < MAX_PLAYERS; i++)
10508       {
10509         if (trigger_player_bits & (1 << i))
10510         {
10511           stored_player[i].gravity =
10512             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10513              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10514              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10515              stored_player[i].gravity);
10516         }
10517       }
10518
10519       break;
10520     }
10521 #endif
10522
10523     case CA_SET_PLAYER_ARTWORK:
10524     {
10525       for (i = 0; i < MAX_PLAYERS; i++)
10526       {
10527         if (trigger_player_bits & (1 << i))
10528         {
10529           int artwork_element = action_arg_element;
10530
10531           if (action_arg == CA_ARG_ELEMENT_RESET)
10532             artwork_element =
10533               (level.use_artwork_element[i] ? level.artwork_element[i] :
10534                stored_player[i].element_nr);
10535
10536 #if USE_GFX_RESET_PLAYER_ARTWORK
10537           if (stored_player[i].artwork_element != artwork_element)
10538             stored_player[i].Frame = 0;
10539 #endif
10540
10541           stored_player[i].artwork_element = artwork_element;
10542
10543           SetPlayerWaiting(&stored_player[i], FALSE);
10544
10545           /* set number of special actions for bored and sleeping animation */
10546           stored_player[i].num_special_action_bored =
10547             get_num_special_action(artwork_element,
10548                                    ACTION_BORING_1, ACTION_BORING_LAST);
10549           stored_player[i].num_special_action_sleeping =
10550             get_num_special_action(artwork_element,
10551                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10552         }
10553       }
10554
10555       break;
10556     }
10557
10558     case CA_SET_PLAYER_INVENTORY:
10559     {
10560       for (i = 0; i < MAX_PLAYERS; i++)
10561       {
10562         struct PlayerInfo *player = &stored_player[i];
10563         int j, k;
10564
10565         if (trigger_player_bits & (1 << i))
10566         {
10567           int inventory_element = action_arg_element;
10568
10569           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10570               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10571               action_arg == CA_ARG_ELEMENT_ACTION)
10572           {
10573             int element = inventory_element;
10574             int collect_count = element_info[element].collect_count_initial;
10575
10576             if (!IS_CUSTOM_ELEMENT(element))
10577               collect_count = 1;
10578
10579             if (collect_count == 0)
10580               player->inventory_infinite_element = element;
10581             else
10582               for (k = 0; k < collect_count; k++)
10583                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10584                   player->inventory_element[player->inventory_size++] =
10585                     element;
10586           }
10587           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10588                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10589                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10590           {
10591             if (player->inventory_infinite_element != EL_UNDEFINED &&
10592                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10593                                      action_arg_element_raw))
10594               player->inventory_infinite_element = EL_UNDEFINED;
10595
10596             for (k = 0, j = 0; j < player->inventory_size; j++)
10597             {
10598               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10599                                         action_arg_element_raw))
10600                 player->inventory_element[k++] = player->inventory_element[j];
10601             }
10602
10603             player->inventory_size = k;
10604           }
10605           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10606           {
10607             if (player->inventory_size > 0)
10608             {
10609               for (j = 0; j < player->inventory_size - 1; j++)
10610                 player->inventory_element[j] = player->inventory_element[j + 1];
10611
10612               player->inventory_size--;
10613             }
10614           }
10615           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10616           {
10617             if (player->inventory_size > 0)
10618               player->inventory_size--;
10619           }
10620           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10621           {
10622             player->inventory_infinite_element = EL_UNDEFINED;
10623             player->inventory_size = 0;
10624           }
10625           else if (action_arg == CA_ARG_INVENTORY_RESET)
10626           {
10627             player->inventory_infinite_element = EL_UNDEFINED;
10628             player->inventory_size = 0;
10629
10630             if (level.use_initial_inventory[i])
10631             {
10632               for (j = 0; j < level.initial_inventory_size[i]; j++)
10633               {
10634                 int element = level.initial_inventory_content[i][j];
10635                 int collect_count = element_info[element].collect_count_initial;
10636
10637                 if (!IS_CUSTOM_ELEMENT(element))
10638                   collect_count = 1;
10639
10640                 if (collect_count == 0)
10641                   player->inventory_infinite_element = element;
10642                 else
10643                   for (k = 0; k < collect_count; k++)
10644                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10645                       player->inventory_element[player->inventory_size++] =
10646                         element;
10647               }
10648             }
10649           }
10650         }
10651       }
10652
10653       break;
10654     }
10655
10656     /* ---------- CE actions  ---------------------------------------------- */
10657
10658     case CA_SET_CE_VALUE:
10659     {
10660 #if USE_NEW_CUSTOM_VALUE
10661       int last_ce_value = CustomValue[x][y];
10662
10663       CustomValue[x][y] = action_arg_number_new;
10664
10665       if (CustomValue[x][y] != last_ce_value)
10666       {
10667         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10668         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10669
10670         if (CustomValue[x][y] == 0)
10671         {
10672           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10673           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10674         }
10675       }
10676 #endif
10677
10678       break;
10679     }
10680
10681     case CA_SET_CE_SCORE:
10682     {
10683 #if USE_NEW_CUSTOM_VALUE
10684       int last_ce_score = ei->collect_score;
10685
10686       ei->collect_score = action_arg_number_new;
10687
10688       if (ei->collect_score != last_ce_score)
10689       {
10690         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10691         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10692
10693         if (ei->collect_score == 0)
10694         {
10695           int xx, yy;
10696
10697           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10698           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10699
10700           /*
10701             This is a very special case that seems to be a mixture between
10702             CheckElementChange() and CheckTriggeredElementChange(): while
10703             the first one only affects single elements that are triggered
10704             directly, the second one affects multiple elements in the playfield
10705             that are triggered indirectly by another element. This is a third
10706             case: Changing the CE score always affects multiple identical CEs,
10707             so every affected CE must be checked, not only the single CE for
10708             which the CE score was changed in the first place (as every instance
10709             of that CE shares the same CE score, and therefore also can change)!
10710           */
10711           SCAN_PLAYFIELD(xx, yy)
10712           {
10713             if (Feld[xx][yy] == element)
10714               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10715                                  CE_SCORE_GETS_ZERO);
10716           }
10717         }
10718       }
10719 #endif
10720
10721       break;
10722     }
10723
10724     case CA_SET_CE_ARTWORK:
10725     {
10726       int artwork_element = action_arg_element;
10727       boolean reset_frame = FALSE;
10728       int xx, yy;
10729
10730       if (action_arg == CA_ARG_ELEMENT_RESET)
10731         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10732                            element);
10733
10734       if (ei->gfx_element != artwork_element)
10735         reset_frame = TRUE;
10736
10737       ei->gfx_element = artwork_element;
10738
10739       SCAN_PLAYFIELD(xx, yy)
10740       {
10741         if (Feld[xx][yy] == element)
10742         {
10743           if (reset_frame)
10744           {
10745             ResetGfxAnimation(xx, yy);
10746             ResetRandomAnimationValue(xx, yy);
10747           }
10748
10749           TEST_DrawLevelField(xx, yy);
10750         }
10751       }
10752
10753       break;
10754     }
10755
10756     /* ---------- engine actions  ------------------------------------------ */
10757
10758     case CA_SET_ENGINE_SCAN_MODE:
10759     {
10760       InitPlayfieldScanMode(action_arg);
10761
10762       break;
10763     }
10764
10765     default:
10766       break;
10767   }
10768 }
10769
10770 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10771 {
10772   int old_element = Feld[x][y];
10773   int new_element = GetElementFromGroupElement(element);
10774   int previous_move_direction = MovDir[x][y];
10775 #if USE_NEW_CUSTOM_VALUE
10776   int last_ce_value = CustomValue[x][y];
10777 #endif
10778   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10779   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10780   boolean add_player_onto_element = (new_element_is_player &&
10781 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10782                                      /* this breaks SnakeBite when a snake is
10783                                         halfway through a door that closes */
10784                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10785                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10786 #endif
10787                                      IS_WALKABLE(old_element));
10788
10789 #if 0
10790   /* check if element under the player changes from accessible to unaccessible
10791      (needed for special case of dropping element which then changes) */
10792   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10793       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10794   {
10795     Bang(x, y);
10796
10797     return;
10798   }
10799 #endif
10800
10801   if (!add_player_onto_element)
10802   {
10803     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10804       RemoveMovingField(x, y);
10805     else
10806       RemoveField(x, y);
10807
10808     Feld[x][y] = new_element;
10809
10810 #if !USE_GFX_RESET_GFX_ANIMATION
10811     ResetGfxAnimation(x, y);
10812     ResetRandomAnimationValue(x, y);
10813 #endif
10814
10815     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10816       MovDir[x][y] = previous_move_direction;
10817
10818 #if USE_NEW_CUSTOM_VALUE
10819     if (element_info[new_element].use_last_ce_value)
10820       CustomValue[x][y] = last_ce_value;
10821 #endif
10822
10823     InitField_WithBug1(x, y, FALSE);
10824
10825     new_element = Feld[x][y];   /* element may have changed */
10826
10827 #if USE_GFX_RESET_GFX_ANIMATION
10828     ResetGfxAnimation(x, y);
10829     ResetRandomAnimationValue(x, y);
10830 #endif
10831
10832     TEST_DrawLevelField(x, y);
10833
10834     if (GFX_CRUMBLED(new_element))
10835       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10836   }
10837
10838 #if 1
10839   /* check if element under the player changes from accessible to unaccessible
10840      (needed for special case of dropping element which then changes) */
10841   /* (must be checked after creating new element for walkable group elements) */
10842 #if USE_FIX_KILLED_BY_NON_WALKABLE
10843   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10844       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10845   {
10846     Bang(x, y);
10847
10848     return;
10849   }
10850 #else
10851   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10852       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10853   {
10854     Bang(x, y);
10855
10856     return;
10857   }
10858 #endif
10859 #endif
10860
10861   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10862   if (new_element_is_player)
10863     RelocatePlayer(x, y, new_element);
10864
10865   if (is_change)
10866     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10867
10868   TestIfBadThingTouchesPlayer(x, y);
10869   TestIfPlayerTouchesCustomElement(x, y);
10870   TestIfElementTouchesCustomElement(x, y);
10871 }
10872
10873 static void CreateField(int x, int y, int element)
10874 {
10875   CreateFieldExt(x, y, element, FALSE);
10876 }
10877
10878 static void CreateElementFromChange(int x, int y, int element)
10879 {
10880   element = GET_VALID_RUNTIME_ELEMENT(element);
10881
10882 #if USE_STOP_CHANGED_ELEMENTS
10883   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10884   {
10885     int old_element = Feld[x][y];
10886
10887     /* prevent changed element from moving in same engine frame
10888        unless both old and new element can either fall or move */
10889     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10890         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10891       Stop[x][y] = TRUE;
10892   }
10893 #endif
10894
10895   CreateFieldExt(x, y, element, TRUE);
10896 }
10897
10898 static boolean ChangeElement(int x, int y, int element, int page)
10899 {
10900   struct ElementInfo *ei = &element_info[element];
10901   struct ElementChangeInfo *change = &ei->change_page[page];
10902   int ce_value = CustomValue[x][y];
10903   int ce_score = ei->collect_score;
10904   int target_element;
10905   int old_element = Feld[x][y];
10906
10907   /* always use default change event to prevent running into a loop */
10908   if (ChangeEvent[x][y] == -1)
10909     ChangeEvent[x][y] = CE_DELAY;
10910
10911   if (ChangeEvent[x][y] == CE_DELAY)
10912   {
10913     /* reset actual trigger element, trigger player and action element */
10914     change->actual_trigger_element = EL_EMPTY;
10915     change->actual_trigger_player = EL_EMPTY;
10916     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10917     change->actual_trigger_side = CH_SIDE_NONE;
10918     change->actual_trigger_ce_value = 0;
10919     change->actual_trigger_ce_score = 0;
10920   }
10921
10922   /* do not change elements more than a specified maximum number of changes */
10923   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10924     return FALSE;
10925
10926   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10927
10928   if (change->explode)
10929   {
10930     Bang(x, y);
10931
10932     return TRUE;
10933   }
10934
10935   if (change->use_target_content)
10936   {
10937     boolean complete_replace = TRUE;
10938     boolean can_replace[3][3];
10939     int xx, yy;
10940
10941     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10942     {
10943       boolean is_empty;
10944       boolean is_walkable;
10945       boolean is_diggable;
10946       boolean is_collectible;
10947       boolean is_removable;
10948       boolean is_destructible;
10949       int ex = x + xx - 1;
10950       int ey = y + yy - 1;
10951       int content_element = change->target_content.e[xx][yy];
10952       int e;
10953
10954       can_replace[xx][yy] = TRUE;
10955
10956       if (ex == x && ey == y)   /* do not check changing element itself */
10957         continue;
10958
10959       if (content_element == EL_EMPTY_SPACE)
10960       {
10961         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10962
10963         continue;
10964       }
10965
10966       if (!IN_LEV_FIELD(ex, ey))
10967       {
10968         can_replace[xx][yy] = FALSE;
10969         complete_replace = FALSE;
10970
10971         continue;
10972       }
10973
10974       e = Feld[ex][ey];
10975
10976       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10977         e = MovingOrBlocked2Element(ex, ey);
10978
10979       is_empty = (IS_FREE(ex, ey) ||
10980                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10981
10982       is_walkable     = (is_empty || IS_WALKABLE(e));
10983       is_diggable     = (is_empty || IS_DIGGABLE(e));
10984       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10985       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10986       is_removable    = (is_diggable || is_collectible);
10987
10988       can_replace[xx][yy] =
10989         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10990           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10991           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10992           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10993           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10994           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10995          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10996
10997       if (!can_replace[xx][yy])
10998         complete_replace = FALSE;
10999     }
11000
11001     if (!change->only_if_complete || complete_replace)
11002     {
11003       boolean something_has_changed = FALSE;
11004
11005       if (change->only_if_complete && change->use_random_replace &&
11006           RND(100) < change->random_percentage)
11007         return FALSE;
11008
11009       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11010       {
11011         int ex = x + xx - 1;
11012         int ey = y + yy - 1;
11013         int content_element;
11014
11015         if (can_replace[xx][yy] && (!change->use_random_replace ||
11016                                     RND(100) < change->random_percentage))
11017         {
11018           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11019             RemoveMovingField(ex, ey);
11020
11021           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11022
11023           content_element = change->target_content.e[xx][yy];
11024           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11025                                               ce_value, ce_score);
11026
11027           CreateElementFromChange(ex, ey, target_element);
11028
11029           something_has_changed = TRUE;
11030
11031           /* for symmetry reasons, freeze newly created border elements */
11032           if (ex != x || ey != y)
11033             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11034         }
11035       }
11036
11037       if (something_has_changed)
11038       {
11039         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11040         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11041       }
11042     }
11043   }
11044   else
11045   {
11046     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11047                                         ce_value, ce_score);
11048
11049     if (element == EL_DIAGONAL_GROWING ||
11050         element == EL_DIAGONAL_SHRINKING)
11051     {
11052       target_element = Store[x][y];
11053
11054       Store[x][y] = EL_EMPTY;
11055     }
11056
11057     CreateElementFromChange(x, y, target_element);
11058
11059     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11060     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11061   }
11062
11063   /* this uses direct change before indirect change */
11064   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11065
11066   return TRUE;
11067 }
11068
11069 #if USE_NEW_DELAYED_ACTION
11070
11071 static void HandleElementChange(int x, int y, int page)
11072 {
11073   int element = MovingOrBlocked2Element(x, y);
11074   struct ElementInfo *ei = &element_info[element];
11075   struct ElementChangeInfo *change = &ei->change_page[page];
11076
11077 #ifdef DEBUG
11078   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11079       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11080   {
11081     printf("\n\n");
11082     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11083            x, y, element, element_info[element].token_name);
11084     printf("HandleElementChange(): This should never happen!\n");
11085     printf("\n\n");
11086   }
11087 #endif
11088
11089   /* this can happen with classic bombs on walkable, changing elements */
11090   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11091   {
11092 #if 0
11093     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11094       ChangeDelay[x][y] = 0;
11095 #endif
11096
11097     return;
11098   }
11099
11100   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11101   {
11102     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11103
11104     if (change->can_change)
11105     {
11106 #if 1
11107       /* !!! not clear why graphic animation should be reset at all here !!! */
11108       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11109 #if USE_GFX_RESET_WHEN_NOT_MOVING
11110       /* when a custom element is about to change (for example by change delay),
11111          do not reset graphic animation when the custom element is moving */
11112       if (!IS_MOVING(x, y))
11113 #endif
11114       {
11115         ResetGfxAnimation(x, y);
11116         ResetRandomAnimationValue(x, y);
11117       }
11118 #endif
11119
11120       if (change->pre_change_function)
11121         change->pre_change_function(x, y);
11122     }
11123   }
11124
11125   ChangeDelay[x][y]--;
11126
11127   if (ChangeDelay[x][y] != 0)           /* continue element change */
11128   {
11129     if (change->can_change)
11130     {
11131       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11132
11133       if (IS_ANIMATED(graphic))
11134         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11135
11136       if (change->change_function)
11137         change->change_function(x, y);
11138     }
11139   }
11140   else                                  /* finish element change */
11141   {
11142     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11143     {
11144       page = ChangePage[x][y];
11145       ChangePage[x][y] = -1;
11146
11147       change = &ei->change_page[page];
11148     }
11149
11150     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11151     {
11152       ChangeDelay[x][y] = 1;            /* try change after next move step */
11153       ChangePage[x][y] = page;          /* remember page to use for change */
11154
11155       return;
11156     }
11157
11158     if (change->can_change)
11159     {
11160       if (ChangeElement(x, y, element, page))
11161       {
11162         if (change->post_change_function)
11163           change->post_change_function(x, y);
11164       }
11165     }
11166
11167     if (change->has_action)
11168       ExecuteCustomElementAction(x, y, element, page);
11169   }
11170 }
11171
11172 #else
11173
11174 static void HandleElementChange(int x, int y, int page)
11175 {
11176   int element = MovingOrBlocked2Element(x, y);
11177   struct ElementInfo *ei = &element_info[element];
11178   struct ElementChangeInfo *change = &ei->change_page[page];
11179
11180 #ifdef DEBUG
11181   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11182   {
11183     printf("\n\n");
11184     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11185            x, y, element, element_info[element].token_name);
11186     printf("HandleElementChange(): This should never happen!\n");
11187     printf("\n\n");
11188   }
11189 #endif
11190
11191   /* this can happen with classic bombs on walkable, changing elements */
11192   if (!CAN_CHANGE(element))
11193   {
11194 #if 0
11195     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11196       ChangeDelay[x][y] = 0;
11197 #endif
11198
11199     return;
11200   }
11201
11202   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11203   {
11204     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11205
11206     ResetGfxAnimation(x, y);
11207     ResetRandomAnimationValue(x, y);
11208
11209     if (change->pre_change_function)
11210       change->pre_change_function(x, y);
11211   }
11212
11213   ChangeDelay[x][y]--;
11214
11215   if (ChangeDelay[x][y] != 0)           /* continue element change */
11216   {
11217     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11218
11219     if (IS_ANIMATED(graphic))
11220       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11221
11222     if (change->change_function)
11223       change->change_function(x, y);
11224   }
11225   else                                  /* finish element change */
11226   {
11227     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11228     {
11229       page = ChangePage[x][y];
11230       ChangePage[x][y] = -1;
11231
11232       change = &ei->change_page[page];
11233     }
11234
11235     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11236     {
11237       ChangeDelay[x][y] = 1;            /* try change after next move step */
11238       ChangePage[x][y] = page;          /* remember page to use for change */
11239
11240       return;
11241     }
11242
11243     if (ChangeElement(x, y, element, page))
11244     {
11245       if (change->post_change_function)
11246         change->post_change_function(x, y);
11247     }
11248   }
11249 }
11250
11251 #endif
11252
11253 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11254                                               int trigger_element,
11255                                               int trigger_event,
11256                                               int trigger_player,
11257                                               int trigger_side,
11258                                               int trigger_page)
11259 {
11260   boolean change_done_any = FALSE;
11261   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11262   int i;
11263
11264   if (!(trigger_events[trigger_element][trigger_event]))
11265     return FALSE;
11266
11267 #if 0
11268   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11269          trigger_event, recursion_loop_depth, recursion_loop_detected,
11270          recursion_loop_element, EL_NAME(recursion_loop_element));
11271 #endif
11272
11273   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11274
11275   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11276   {
11277     int element = EL_CUSTOM_START + i;
11278     boolean change_done = FALSE;
11279     int p;
11280
11281     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11282         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11283       continue;
11284
11285     for (p = 0; p < element_info[element].num_change_pages; p++)
11286     {
11287       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11288
11289       if (change->can_change_or_has_action &&
11290           change->has_event[trigger_event] &&
11291           change->trigger_side & trigger_side &&
11292           change->trigger_player & trigger_player &&
11293           change->trigger_page & trigger_page_bits &&
11294           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11295       {
11296         change->actual_trigger_element = trigger_element;
11297         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11298         change->actual_trigger_player_bits = trigger_player;
11299         change->actual_trigger_side = trigger_side;
11300         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11301         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11302
11303 #if 0
11304         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11305                element, EL_NAME(element), p);
11306 #endif
11307
11308         if ((change->can_change && !change_done) || change->has_action)
11309         {
11310           int x, y;
11311
11312           SCAN_PLAYFIELD(x, y)
11313           {
11314             if (Feld[x][y] == element)
11315             {
11316               if (change->can_change && !change_done)
11317               {
11318 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11319                 /* if element already changed in this frame, not only prevent
11320                    another element change (checked in ChangeElement()), but
11321                    also prevent additional element actions for this element */
11322
11323                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11324                     !level.use_action_after_change_bug)
11325                   continue;
11326 #endif
11327
11328 #if 0
11329                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11330                        element, EL_NAME(element), p);
11331 #endif
11332
11333                 ChangeDelay[x][y] = 1;
11334                 ChangeEvent[x][y] = trigger_event;
11335
11336                 HandleElementChange(x, y, p);
11337               }
11338 #if USE_NEW_DELAYED_ACTION
11339               else if (change->has_action)
11340               {
11341 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11342                 /* if element already changed in this frame, not only prevent
11343                    another element change (checked in ChangeElement()), but
11344                    also prevent additional element actions for this element */
11345
11346                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11347                     !level.use_action_after_change_bug)
11348                   continue;
11349 #endif
11350
11351
11352 #if 0
11353                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11354                        element, EL_NAME(element), p);
11355 #endif
11356
11357                 ExecuteCustomElementAction(x, y, element, p);
11358                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11359               }
11360 #else
11361               if (change->has_action)
11362               {
11363                 ExecuteCustomElementAction(x, y, element, p);
11364                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11365               }
11366 #endif
11367             }
11368           }
11369
11370           if (change->can_change)
11371           {
11372             change_done = TRUE;
11373             change_done_any = TRUE;
11374
11375 #if 0
11376             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11377                    element, EL_NAME(element), p);
11378 #endif
11379
11380           }
11381         }
11382       }
11383     }
11384   }
11385
11386   RECURSION_LOOP_DETECTION_END();
11387
11388   return change_done_any;
11389 }
11390
11391 static boolean CheckElementChangeExt(int x, int y,
11392                                      int element,
11393                                      int trigger_element,
11394                                      int trigger_event,
11395                                      int trigger_player,
11396                                      int trigger_side)
11397 {
11398   boolean change_done = FALSE;
11399   int p;
11400
11401   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11402       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11403     return FALSE;
11404
11405   if (Feld[x][y] == EL_BLOCKED)
11406   {
11407     Blocked2Moving(x, y, &x, &y);
11408     element = Feld[x][y];
11409   }
11410
11411 #if 0
11412   /* check if element has already changed */
11413   if (Feld[x][y] != element)
11414     return FALSE;
11415 #else
11416   /* check if element has already changed or is about to change after moving */
11417   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11418        Feld[x][y] != element) ||
11419
11420       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11421        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11422         ChangePage[x][y] != -1)))
11423     return FALSE;
11424 #endif
11425
11426 #if 0
11427   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11428          trigger_event, recursion_loop_depth, recursion_loop_detected,
11429          recursion_loop_element, EL_NAME(recursion_loop_element));
11430 #endif
11431
11432   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11433
11434 #if 0
11435   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11436 #endif
11437
11438   for (p = 0; p < element_info[element].num_change_pages; p++)
11439   {
11440     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11441
11442     /* check trigger element for all events where the element that is checked
11443        for changing interacts with a directly adjacent element -- this is
11444        different to element changes that affect other elements to change on the
11445        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11446     boolean check_trigger_element =
11447       (trigger_event == CE_TOUCHING_X ||
11448        trigger_event == CE_HITTING_X ||
11449        trigger_event == CE_HIT_BY_X ||
11450 #if 1
11451        /* this one was forgotten until 3.2.3 */
11452        trigger_event == CE_DIGGING_X);
11453 #endif
11454
11455     if (change->can_change_or_has_action &&
11456         change->has_event[trigger_event] &&
11457         change->trigger_side & trigger_side &&
11458         change->trigger_player & trigger_player &&
11459         (!check_trigger_element ||
11460          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11461     {
11462       change->actual_trigger_element = trigger_element;
11463       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11464       change->actual_trigger_player_bits = trigger_player;
11465       change->actual_trigger_side = trigger_side;
11466       change->actual_trigger_ce_value = CustomValue[x][y];
11467       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11468
11469       /* special case: trigger element not at (x,y) position for some events */
11470       if (check_trigger_element)
11471       {
11472         static struct
11473         {
11474           int dx, dy;
11475         } move_xy[] =
11476           {
11477             {  0,  0 },
11478             { -1,  0 },
11479             { +1,  0 },
11480             {  0,  0 },
11481             {  0, -1 },
11482             {  0,  0 }, { 0, 0 }, { 0, 0 },
11483             {  0, +1 }
11484           };
11485
11486         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11487         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11488
11489         change->actual_trigger_ce_value = CustomValue[xx][yy];
11490         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11491       }
11492
11493       if (change->can_change && !change_done)
11494       {
11495         ChangeDelay[x][y] = 1;
11496         ChangeEvent[x][y] = trigger_event;
11497
11498         HandleElementChange(x, y, p);
11499
11500         change_done = TRUE;
11501       }
11502 #if USE_NEW_DELAYED_ACTION
11503       else if (change->has_action)
11504       {
11505         ExecuteCustomElementAction(x, y, element, p);
11506         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11507       }
11508 #else
11509       if (change->has_action)
11510       {
11511         ExecuteCustomElementAction(x, y, element, p);
11512         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11513       }
11514 #endif
11515     }
11516   }
11517
11518   RECURSION_LOOP_DETECTION_END();
11519
11520   return change_done;
11521 }
11522
11523 static void PlayPlayerSound(struct PlayerInfo *player)
11524 {
11525   int jx = player->jx, jy = player->jy;
11526   int sound_element = player->artwork_element;
11527   int last_action = player->last_action_waiting;
11528   int action = player->action_waiting;
11529
11530   if (player->is_waiting)
11531   {
11532     if (action != last_action)
11533       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11534     else
11535       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11536   }
11537   else
11538   {
11539     if (action != last_action)
11540       StopSound(element_info[sound_element].sound[last_action]);
11541
11542     if (last_action == ACTION_SLEEPING)
11543       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11544   }
11545 }
11546
11547 static void PlayAllPlayersSound()
11548 {
11549   int i;
11550
11551   for (i = 0; i < MAX_PLAYERS; i++)
11552     if (stored_player[i].active)
11553       PlayPlayerSound(&stored_player[i]);
11554 }
11555
11556 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11557 {
11558   boolean last_waiting = player->is_waiting;
11559   int move_dir = player->MovDir;
11560
11561   player->dir_waiting = move_dir;
11562   player->last_action_waiting = player->action_waiting;
11563
11564   if (is_waiting)
11565   {
11566     if (!last_waiting)          /* not waiting -> waiting */
11567     {
11568       player->is_waiting = TRUE;
11569
11570       player->frame_counter_bored =
11571         FrameCounter +
11572         game.player_boring_delay_fixed +
11573         GetSimpleRandom(game.player_boring_delay_random);
11574       player->frame_counter_sleeping =
11575         FrameCounter +
11576         game.player_sleeping_delay_fixed +
11577         GetSimpleRandom(game.player_sleeping_delay_random);
11578
11579       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11580     }
11581
11582     if (game.player_sleeping_delay_fixed +
11583         game.player_sleeping_delay_random > 0 &&
11584         player->anim_delay_counter == 0 &&
11585         player->post_delay_counter == 0 &&
11586         FrameCounter >= player->frame_counter_sleeping)
11587       player->is_sleeping = TRUE;
11588     else if (game.player_boring_delay_fixed +
11589              game.player_boring_delay_random > 0 &&
11590              FrameCounter >= player->frame_counter_bored)
11591       player->is_bored = TRUE;
11592
11593     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11594                               player->is_bored ? ACTION_BORING :
11595                               ACTION_WAITING);
11596
11597     if (player->is_sleeping && player->use_murphy)
11598     {
11599       /* special case for sleeping Murphy when leaning against non-free tile */
11600
11601       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11602           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11603            !IS_MOVING(player->jx - 1, player->jy)))
11604         move_dir = MV_LEFT;
11605       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11606                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11607                 !IS_MOVING(player->jx + 1, player->jy)))
11608         move_dir = MV_RIGHT;
11609       else
11610         player->is_sleeping = FALSE;
11611
11612       player->dir_waiting = move_dir;
11613     }
11614
11615     if (player->is_sleeping)
11616     {
11617       if (player->num_special_action_sleeping > 0)
11618       {
11619         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11620         {
11621           int last_special_action = player->special_action_sleeping;
11622           int num_special_action = player->num_special_action_sleeping;
11623           int special_action =
11624             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11625              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11626              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11627              last_special_action + 1 : ACTION_SLEEPING);
11628           int special_graphic =
11629             el_act_dir2img(player->artwork_element, special_action, move_dir);
11630
11631           player->anim_delay_counter =
11632             graphic_info[special_graphic].anim_delay_fixed +
11633             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11634           player->post_delay_counter =
11635             graphic_info[special_graphic].post_delay_fixed +
11636             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11637
11638           player->special_action_sleeping = special_action;
11639         }
11640
11641         if (player->anim_delay_counter > 0)
11642         {
11643           player->action_waiting = player->special_action_sleeping;
11644           player->anim_delay_counter--;
11645         }
11646         else if (player->post_delay_counter > 0)
11647         {
11648           player->post_delay_counter--;
11649         }
11650       }
11651     }
11652     else if (player->is_bored)
11653     {
11654       if (player->num_special_action_bored > 0)
11655       {
11656         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11657         {
11658           int special_action =
11659             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11660           int special_graphic =
11661             el_act_dir2img(player->artwork_element, special_action, move_dir);
11662
11663           player->anim_delay_counter =
11664             graphic_info[special_graphic].anim_delay_fixed +
11665             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11666           player->post_delay_counter =
11667             graphic_info[special_graphic].post_delay_fixed +
11668             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11669
11670           player->special_action_bored = special_action;
11671         }
11672
11673         if (player->anim_delay_counter > 0)
11674         {
11675           player->action_waiting = player->special_action_bored;
11676           player->anim_delay_counter--;
11677         }
11678         else if (player->post_delay_counter > 0)
11679         {
11680           player->post_delay_counter--;
11681         }
11682       }
11683     }
11684   }
11685   else if (last_waiting)        /* waiting -> not waiting */
11686   {
11687     player->is_waiting = FALSE;
11688     player->is_bored = FALSE;
11689     player->is_sleeping = FALSE;
11690
11691     player->frame_counter_bored = -1;
11692     player->frame_counter_sleeping = -1;
11693
11694     player->anim_delay_counter = 0;
11695     player->post_delay_counter = 0;
11696
11697     player->dir_waiting = player->MovDir;
11698     player->action_waiting = ACTION_DEFAULT;
11699
11700     player->special_action_bored = ACTION_DEFAULT;
11701     player->special_action_sleeping = ACTION_DEFAULT;
11702   }
11703 }
11704
11705 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11706 {
11707   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11708   int left      = player_action & JOY_LEFT;
11709   int right     = player_action & JOY_RIGHT;
11710   int up        = player_action & JOY_UP;
11711   int down      = player_action & JOY_DOWN;
11712   int button1   = player_action & JOY_BUTTON_1;
11713   int button2   = player_action & JOY_BUTTON_2;
11714   int dx        = (left ? -1 : right ? 1 : 0);
11715   int dy        = (up   ? -1 : down  ? 1 : 0);
11716
11717   if (!player->active || tape.pausing)
11718     return 0;
11719
11720   if (player_action)
11721   {
11722     if (button1)
11723       snapped = SnapField(player, dx, dy);
11724     else
11725     {
11726       if (button2)
11727         dropped = DropElement(player);
11728
11729       moved = MovePlayer(player, dx, dy);
11730     }
11731
11732     if (tape.single_step && tape.recording && !tape.pausing)
11733     {
11734       if (button1 || (dropped && !moved))
11735       {
11736         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11737         SnapField(player, 0, 0);                /* stop snapping */
11738       }
11739     }
11740
11741     SetPlayerWaiting(player, FALSE);
11742
11743     return player_action;
11744   }
11745   else
11746   {
11747     /* no actions for this player (no input at player's configured device) */
11748
11749     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11750     SnapField(player, 0, 0);
11751     CheckGravityMovementWhenNotMoving(player);
11752
11753     if (player->MovPos == 0)
11754       SetPlayerWaiting(player, TRUE);
11755
11756     if (player->MovPos == 0)    /* needed for tape.playing */
11757       player->is_moving = FALSE;
11758
11759     player->is_dropping = FALSE;
11760     player->is_dropping_pressed = FALSE;
11761     player->drop_pressed_delay = 0;
11762
11763     return 0;
11764   }
11765 }
11766
11767 static void CheckLevelTime()
11768 {
11769   int i;
11770
11771   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11772   {
11773     if (level.native_em_level->lev->home == 0)  /* all players at home */
11774     {
11775       PlayerWins(local_player);
11776
11777       AllPlayersGone = TRUE;
11778
11779       level.native_em_level->lev->home = -1;
11780     }
11781
11782     if (level.native_em_level->ply[0]->alive == 0 &&
11783         level.native_em_level->ply[1]->alive == 0 &&
11784         level.native_em_level->ply[2]->alive == 0 &&
11785         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11786       AllPlayersGone = TRUE;
11787   }
11788
11789   if (TimeFrames >= FRAMES_PER_SECOND)
11790   {
11791     TimeFrames = 0;
11792     TapeTime++;
11793
11794     for (i = 0; i < MAX_PLAYERS; i++)
11795     {
11796       struct PlayerInfo *player = &stored_player[i];
11797
11798       if (SHIELD_ON(player))
11799       {
11800         player->shield_normal_time_left--;
11801
11802         if (player->shield_deadly_time_left > 0)
11803           player->shield_deadly_time_left--;
11804       }
11805     }
11806
11807     if (!local_player->LevelSolved && !level.use_step_counter)
11808     {
11809       TimePlayed++;
11810
11811       if (TimeLeft > 0)
11812       {
11813         TimeLeft--;
11814
11815         if (TimeLeft <= 10 && setup.time_limit)
11816           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11817
11818 #if 1
11819         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11820
11821         DisplayGameControlValues();
11822 #else
11823         DrawGameValue_Time(TimeLeft);
11824 #endif
11825
11826         if (!TimeLeft && setup.time_limit)
11827         {
11828           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11829             level.native_em_level->lev->killed_out_of_time = TRUE;
11830           else
11831             for (i = 0; i < MAX_PLAYERS; i++)
11832               KillPlayer(&stored_player[i]);
11833         }
11834       }
11835 #if 1
11836       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11837       {
11838         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11839
11840         DisplayGameControlValues();
11841       }
11842 #else
11843       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11844         DrawGameValue_Time(TimePlayed);
11845 #endif
11846
11847       level.native_em_level->lev->time =
11848         (level.time == 0 ? TimePlayed : TimeLeft);
11849     }
11850
11851     if (tape.recording || tape.playing)
11852       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11853   }
11854
11855 #if 1
11856   UpdateAndDisplayGameControlValues();
11857 #else
11858   UpdateGameDoorValues();
11859   DrawGameDoorValues();
11860 #endif
11861 }
11862
11863 void AdvanceFrameAndPlayerCounters(int player_nr)
11864 {
11865   int i;
11866
11867   /* advance frame counters (global frame counter and time frame counter) */
11868   FrameCounter++;
11869   TimeFrames++;
11870
11871   /* advance player counters (counters for move delay, move animation etc.) */
11872   for (i = 0; i < MAX_PLAYERS; i++)
11873   {
11874     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11875     int move_delay_value = stored_player[i].move_delay_value;
11876     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11877
11878     if (!advance_player_counters)       /* not all players may be affected */
11879       continue;
11880
11881 #if USE_NEW_PLAYER_ANIM
11882     if (move_frames == 0)       /* less than one move per game frame */
11883     {
11884       int stepsize = TILEX / move_delay_value;
11885       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11886       int count = (stored_player[i].is_moving ?
11887                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11888
11889       if (count % delay == 0)
11890         move_frames = 1;
11891     }
11892 #endif
11893
11894     stored_player[i].Frame += move_frames;
11895
11896     if (stored_player[i].MovPos != 0)
11897       stored_player[i].StepFrame += move_frames;
11898
11899     if (stored_player[i].move_delay > 0)
11900       stored_player[i].move_delay--;
11901
11902     /* due to bugs in previous versions, counter must count up, not down */
11903     if (stored_player[i].push_delay != -1)
11904       stored_player[i].push_delay++;
11905
11906     if (stored_player[i].drop_delay > 0)
11907       stored_player[i].drop_delay--;
11908
11909     if (stored_player[i].is_dropping_pressed)
11910       stored_player[i].drop_pressed_delay++;
11911   }
11912 }
11913
11914 void StartGameActions(boolean init_network_game, boolean record_tape,
11915                       long random_seed)
11916 {
11917   unsigned long new_random_seed = InitRND(random_seed);
11918
11919   if (record_tape)
11920     TapeStartRecording(new_random_seed);
11921
11922 #if defined(NETWORK_AVALIABLE)
11923   if (init_network_game)
11924   {
11925     SendToServer_StartPlaying();
11926
11927     return;
11928   }
11929 #endif
11930
11931   InitGame();
11932 }
11933
11934 void GameActions()
11935 {
11936   static unsigned long game_frame_delay = 0;
11937   unsigned long game_frame_delay_value;
11938   byte *recorded_player_action;
11939   byte summarized_player_action = 0;
11940   byte tape_action[MAX_PLAYERS];
11941   int i;
11942
11943   /* detect endless loops, caused by custom element programming */
11944   if (recursion_loop_detected && recursion_loop_depth == 0)
11945   {
11946     char *message = getStringCat3("Internal Error ! Element ",
11947                                   EL_NAME(recursion_loop_element),
11948                                   " caused endless loop ! Quit the game ?");
11949
11950     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11951           EL_NAME(recursion_loop_element));
11952
11953     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11954
11955     recursion_loop_detected = FALSE;    /* if game should be continued */
11956
11957     free(message);
11958
11959     return;
11960   }
11961
11962   if (game.restart_level)
11963     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11964
11965   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11966   {
11967     if (level.native_em_level->lev->home == 0)  /* all players at home */
11968     {
11969       PlayerWins(local_player);
11970
11971       AllPlayersGone = TRUE;
11972
11973       level.native_em_level->lev->home = -1;
11974     }
11975
11976     if (level.native_em_level->ply[0]->alive == 0 &&
11977         level.native_em_level->ply[1]->alive == 0 &&
11978         level.native_em_level->ply[2]->alive == 0 &&
11979         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11980       AllPlayersGone = TRUE;
11981   }
11982
11983   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11984     GameWon();
11985
11986   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11987     TapeStop();
11988
11989   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11990     return;
11991
11992   game_frame_delay_value =
11993     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11994
11995   if (tape.playing && tape.warp_forward && !tape.pausing)
11996     game_frame_delay_value = 0;
11997
11998   /* ---------- main game synchronization point ---------- */
11999
12000   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12001
12002   if (network_playing && !network_player_action_received)
12003   {
12004     /* try to get network player actions in time */
12005
12006 #if defined(NETWORK_AVALIABLE)
12007     /* last chance to get network player actions without main loop delay */
12008     HandleNetworking();
12009 #endif
12010
12011     /* game was quit by network peer */
12012     if (game_status != GAME_MODE_PLAYING)
12013       return;
12014
12015     if (!network_player_action_received)
12016       return;           /* failed to get network player actions in time */
12017
12018     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12019   }
12020
12021   if (tape.pausing)
12022     return;
12023
12024   /* at this point we know that we really continue executing the game */
12025
12026   network_player_action_received = FALSE;
12027
12028   /* when playing tape, read previously recorded player input from tape data */
12029   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12030
12031 #if 1
12032   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12033   if (tape.pausing)
12034     return;
12035 #endif
12036
12037   if (tape.set_centered_player)
12038   {
12039     game.centered_player_nr_next = tape.centered_player_nr_next;
12040     game.set_centered_player = TRUE;
12041   }
12042
12043   for (i = 0; i < MAX_PLAYERS; i++)
12044   {
12045     summarized_player_action |= stored_player[i].action;
12046
12047     if (!network_playing)
12048       stored_player[i].effective_action = stored_player[i].action;
12049   }
12050
12051 #if defined(NETWORK_AVALIABLE)
12052   if (network_playing)
12053     SendToServer_MovePlayer(summarized_player_action);
12054 #endif
12055
12056   if (!options.network && !setup.team_mode)
12057     local_player->effective_action = summarized_player_action;
12058
12059   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12060   {
12061     for (i = 0; i < MAX_PLAYERS; i++)
12062       stored_player[i].effective_action =
12063         (i == game.centered_player_nr ? summarized_player_action : 0);
12064   }
12065
12066   if (recorded_player_action != NULL)
12067     for (i = 0; i < MAX_PLAYERS; i++)
12068       stored_player[i].effective_action = recorded_player_action[i];
12069
12070   for (i = 0; i < MAX_PLAYERS; i++)
12071   {
12072     tape_action[i] = stored_player[i].effective_action;
12073
12074     /* (this can only happen in the R'n'D game engine) */
12075     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12076       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12077   }
12078
12079   /* only record actions from input devices, but not programmed actions */
12080   if (tape.recording)
12081     TapeRecordAction(tape_action);
12082
12083   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12084   {
12085     GameActions_EM_Main();
12086   }
12087   else
12088   {
12089     GameActions_RND();
12090   }
12091 }
12092
12093 void GameActions_EM_Main()
12094 {
12095   byte effective_action[MAX_PLAYERS];
12096   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12097   int i;
12098
12099   for (i = 0; i < MAX_PLAYERS; i++)
12100     effective_action[i] = stored_player[i].effective_action;
12101
12102   GameActions_EM(effective_action, warp_mode);
12103
12104   CheckLevelTime();
12105
12106   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12107 }
12108
12109 void GameActions_RND()
12110 {
12111   int magic_wall_x = 0, magic_wall_y = 0;
12112   int i, x, y, element, graphic;
12113
12114   InitPlayfieldScanModeVars();
12115
12116 #if USE_ONE_MORE_CHANGE_PER_FRAME
12117   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12118   {
12119     SCAN_PLAYFIELD(x, y)
12120     {
12121       ChangeCount[x][y] = 0;
12122       ChangeEvent[x][y] = -1;
12123     }
12124   }
12125 #endif
12126
12127   if (game.set_centered_player)
12128   {
12129     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12130
12131     /* switching to "all players" only possible if all players fit to screen */
12132     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12133     {
12134       game.centered_player_nr_next = game.centered_player_nr;
12135       game.set_centered_player = FALSE;
12136     }
12137
12138     /* do not switch focus to non-existing (or non-active) player */
12139     if (game.centered_player_nr_next >= 0 &&
12140         !stored_player[game.centered_player_nr_next].active)
12141     {
12142       game.centered_player_nr_next = game.centered_player_nr;
12143       game.set_centered_player = FALSE;
12144     }
12145   }
12146
12147   if (game.set_centered_player &&
12148       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12149   {
12150     int sx, sy;
12151
12152     if (game.centered_player_nr_next == -1)
12153     {
12154       setScreenCenteredToAllPlayers(&sx, &sy);
12155     }
12156     else
12157     {
12158       sx = stored_player[game.centered_player_nr_next].jx;
12159       sy = stored_player[game.centered_player_nr_next].jy;
12160     }
12161
12162     game.centered_player_nr = game.centered_player_nr_next;
12163     game.set_centered_player = FALSE;
12164
12165     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12166     DrawGameDoorValues();
12167   }
12168
12169   for (i = 0; i < MAX_PLAYERS; i++)
12170   {
12171     int actual_player_action = stored_player[i].effective_action;
12172
12173 #if 1
12174     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12175        - rnd_equinox_tetrachloride 048
12176        - rnd_equinox_tetrachloride_ii 096
12177        - rnd_emanuel_schmieg 002
12178        - doctor_sloan_ww 001, 020
12179     */
12180     if (stored_player[i].MovPos == 0)
12181       CheckGravityMovement(&stored_player[i]);
12182 #endif
12183
12184     /* overwrite programmed action with tape action */
12185     if (stored_player[i].programmed_action)
12186       actual_player_action = stored_player[i].programmed_action;
12187
12188     PlayerActions(&stored_player[i], actual_player_action);
12189
12190     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12191   }
12192
12193   ScrollScreen(NULL, SCROLL_GO_ON);
12194
12195   /* for backwards compatibility, the following code emulates a fixed bug that
12196      occured when pushing elements (causing elements that just made their last
12197      pushing step to already (if possible) make their first falling step in the
12198      same game frame, which is bad); this code is also needed to use the famous
12199      "spring push bug" which is used in older levels and might be wanted to be
12200      used also in newer levels, but in this case the buggy pushing code is only
12201      affecting the "spring" element and no other elements */
12202
12203   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12204   {
12205     for (i = 0; i < MAX_PLAYERS; i++)
12206     {
12207       struct PlayerInfo *player = &stored_player[i];
12208       int x = player->jx;
12209       int y = player->jy;
12210
12211       if (player->active && player->is_pushing && player->is_moving &&
12212           IS_MOVING(x, y) &&
12213           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12214            Feld[x][y] == EL_SPRING))
12215       {
12216         ContinueMoving(x, y);
12217
12218         /* continue moving after pushing (this is actually a bug) */
12219         if (!IS_MOVING(x, y))
12220           Stop[x][y] = FALSE;
12221       }
12222     }
12223   }
12224
12225 #if 0
12226   debug_print_timestamp(0, "start main loop profiling");
12227 #endif
12228
12229   SCAN_PLAYFIELD(x, y)
12230   {
12231     ChangeCount[x][y] = 0;
12232     ChangeEvent[x][y] = -1;
12233
12234     /* this must be handled before main playfield loop */
12235     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12236     {
12237       MovDelay[x][y]--;
12238       if (MovDelay[x][y] <= 0)
12239         RemoveField(x, y);
12240     }
12241
12242 #if USE_NEW_SNAP_DELAY
12243     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12244     {
12245       MovDelay[x][y]--;
12246       if (MovDelay[x][y] <= 0)
12247       {
12248         RemoveField(x, y);
12249         TEST_DrawLevelField(x, y);
12250
12251         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12252       }
12253     }
12254 #endif
12255
12256 #if DEBUG
12257     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12258     {
12259       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12260       printf("GameActions(): This should never happen!\n");
12261
12262       ChangePage[x][y] = -1;
12263     }
12264 #endif
12265
12266     Stop[x][y] = FALSE;
12267     if (WasJustMoving[x][y] > 0)
12268       WasJustMoving[x][y]--;
12269     if (WasJustFalling[x][y] > 0)
12270       WasJustFalling[x][y]--;
12271     if (CheckCollision[x][y] > 0)
12272       CheckCollision[x][y]--;
12273     if (CheckImpact[x][y] > 0)
12274       CheckImpact[x][y]--;
12275
12276     GfxFrame[x][y]++;
12277
12278     /* reset finished pushing action (not done in ContinueMoving() to allow
12279        continuous pushing animation for elements with zero push delay) */
12280     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12281     {
12282       ResetGfxAnimation(x, y);
12283       TEST_DrawLevelField(x, y);
12284     }
12285
12286 #if DEBUG
12287     if (IS_BLOCKED(x, y))
12288     {
12289       int oldx, oldy;
12290
12291       Blocked2Moving(x, y, &oldx, &oldy);
12292       if (!IS_MOVING(oldx, oldy))
12293       {
12294         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12295         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12296         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12297         printf("GameActions(): This should never happen!\n");
12298       }
12299     }
12300 #endif
12301   }
12302
12303 #if 0
12304   debug_print_timestamp(0, "- time for pre-main loop:");
12305 #endif
12306
12307 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12308   SCAN_PLAYFIELD(x, y)
12309   {
12310     element = Feld[x][y];
12311     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12312
12313 #if 1
12314     {
12315 #if 1
12316       int element2 = element;
12317       int graphic2 = graphic;
12318 #else
12319       int element2 = Feld[x][y];
12320       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12321 #endif
12322       int last_gfx_frame = GfxFrame[x][y];
12323
12324       if (graphic_info[graphic2].anim_global_sync)
12325         GfxFrame[x][y] = FrameCounter;
12326       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12327         GfxFrame[x][y] = CustomValue[x][y];
12328       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12329         GfxFrame[x][y] = element_info[element2].collect_score;
12330       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12331         GfxFrame[x][y] = ChangeDelay[x][y];
12332
12333       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12334         DrawLevelGraphicAnimation(x, y, graphic2);
12335     }
12336 #else
12337     ResetGfxFrame(x, y, TRUE);
12338 #endif
12339
12340 #if 1
12341     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12342         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12343       ResetRandomAnimationValue(x, y);
12344 #endif
12345
12346 #if 1
12347     SetRandomAnimationValue(x, y);
12348 #endif
12349
12350 #if 1
12351     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12352 #endif
12353   }
12354 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12355
12356 #if 0
12357   debug_print_timestamp(0, "- time for TEST loop:     -->");
12358 #endif
12359
12360   SCAN_PLAYFIELD(x, y)
12361   {
12362     element = Feld[x][y];
12363     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12364
12365     ResetGfxFrame(x, y, TRUE);
12366
12367     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12368         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12369       ResetRandomAnimationValue(x, y);
12370
12371     SetRandomAnimationValue(x, y);
12372
12373     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12374
12375     if (IS_INACTIVE(element))
12376     {
12377       if (IS_ANIMATED(graphic))
12378         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12379
12380       continue;
12381     }
12382
12383     /* this may take place after moving, so 'element' may have changed */
12384     if (IS_CHANGING(x, y) &&
12385         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12386     {
12387       int page = element_info[element].event_page_nr[CE_DELAY];
12388
12389 #if 1
12390       HandleElementChange(x, y, page);
12391 #else
12392       if (CAN_CHANGE(element))
12393         HandleElementChange(x, y, page);
12394
12395       if (HAS_ACTION(element))
12396         ExecuteCustomElementAction(x, y, element, page);
12397 #endif
12398
12399       element = Feld[x][y];
12400       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12401     }
12402
12403 #if 0   // ---------------------------------------------------------------------
12404
12405     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12406     {
12407       StartMoving(x, y);
12408
12409       element = Feld[x][y];
12410       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12411
12412       if (IS_ANIMATED(graphic) &&
12413           !IS_MOVING(x, y) &&
12414           !Stop[x][y])
12415         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12416
12417       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12418         TEST_DrawTwinkleOnField(x, y);
12419     }
12420     else if (IS_MOVING(x, y))
12421       ContinueMoving(x, y);
12422     else
12423     {
12424       switch (element)
12425       {
12426         case EL_ACID:
12427         case EL_EXIT_OPEN:
12428         case EL_EM_EXIT_OPEN:
12429         case EL_SP_EXIT_OPEN:
12430         case EL_STEEL_EXIT_OPEN:
12431         case EL_EM_STEEL_EXIT_OPEN:
12432         case EL_SP_TERMINAL:
12433         case EL_SP_TERMINAL_ACTIVE:
12434         case EL_EXTRA_TIME:
12435         case EL_SHIELD_NORMAL:
12436         case EL_SHIELD_DEADLY:
12437           if (IS_ANIMATED(graphic))
12438             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12439           break;
12440
12441         case EL_DYNAMITE_ACTIVE:
12442         case EL_EM_DYNAMITE_ACTIVE:
12443         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12444         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12445         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12446         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12447         case EL_SP_DISK_RED_ACTIVE:
12448           CheckDynamite(x, y);
12449           break;
12450
12451         case EL_AMOEBA_GROWING:
12452           AmoebeWaechst(x, y);
12453           break;
12454
12455         case EL_AMOEBA_SHRINKING:
12456           AmoebaDisappearing(x, y);
12457           break;
12458
12459 #if !USE_NEW_AMOEBA_CODE
12460         case EL_AMOEBA_WET:
12461         case EL_AMOEBA_DRY:
12462         case EL_AMOEBA_FULL:
12463         case EL_BD_AMOEBA:
12464         case EL_EMC_DRIPPER:
12465           AmoebeAbleger(x, y);
12466           break;
12467 #endif
12468
12469         case EL_GAME_OF_LIFE:
12470         case EL_BIOMAZE:
12471           Life(x, y);
12472           break;
12473
12474         case EL_EXIT_CLOSED:
12475           CheckExit(x, y);
12476           break;
12477
12478         case EL_EM_EXIT_CLOSED:
12479           CheckExitEM(x, y);
12480           break;
12481
12482         case EL_STEEL_EXIT_CLOSED:
12483           CheckExitSteel(x, y);
12484           break;
12485
12486         case EL_EM_STEEL_EXIT_CLOSED:
12487           CheckExitSteelEM(x, y);
12488           break;
12489
12490         case EL_SP_EXIT_CLOSED:
12491           CheckExitSP(x, y);
12492           break;
12493
12494         case EL_EXPANDABLE_WALL_GROWING:
12495         case EL_EXPANDABLE_STEELWALL_GROWING:
12496           MauerWaechst(x, y);
12497           break;
12498
12499         case EL_EXPANDABLE_WALL:
12500         case EL_EXPANDABLE_WALL_HORIZONTAL:
12501         case EL_EXPANDABLE_WALL_VERTICAL:
12502         case EL_EXPANDABLE_WALL_ANY:
12503         case EL_BD_EXPANDABLE_WALL:
12504           MauerAbleger(x, y);
12505           break;
12506
12507         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12508         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12509         case EL_EXPANDABLE_STEELWALL_ANY:
12510           MauerAblegerStahl(x, y);
12511           break;
12512
12513         case EL_FLAMES:
12514           CheckForDragon(x, y);
12515           break;
12516
12517         case EL_EXPLOSION:
12518           break;
12519
12520         case EL_ELEMENT_SNAPPING:
12521         case EL_DIAGONAL_SHRINKING:
12522         case EL_DIAGONAL_GROWING:
12523         {
12524           graphic =
12525             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12526
12527           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12528           break;
12529         }
12530
12531         default:
12532           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12533             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12534           break;
12535       }
12536     }
12537
12538 #else   // ---------------------------------------------------------------------
12539
12540     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12541     {
12542       StartMoving(x, y);
12543
12544       element = Feld[x][y];
12545       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12546
12547       if (IS_ANIMATED(graphic) &&
12548           !IS_MOVING(x, y) &&
12549           !Stop[x][y])
12550         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12551
12552       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12553         TEST_DrawTwinkleOnField(x, y);
12554     }
12555     else if ((element == EL_ACID ||
12556               element == EL_EXIT_OPEN ||
12557               element == EL_EM_EXIT_OPEN ||
12558               element == EL_SP_EXIT_OPEN ||
12559               element == EL_STEEL_EXIT_OPEN ||
12560               element == EL_EM_STEEL_EXIT_OPEN ||
12561               element == EL_SP_TERMINAL ||
12562               element == EL_SP_TERMINAL_ACTIVE ||
12563               element == EL_EXTRA_TIME ||
12564               element == EL_SHIELD_NORMAL ||
12565               element == EL_SHIELD_DEADLY) &&
12566              IS_ANIMATED(graphic))
12567       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12568     else if (IS_MOVING(x, y))
12569       ContinueMoving(x, y);
12570     else if (IS_ACTIVE_BOMB(element))
12571       CheckDynamite(x, y);
12572     else if (element == EL_AMOEBA_GROWING)
12573       AmoebeWaechst(x, y);
12574     else if (element == EL_AMOEBA_SHRINKING)
12575       AmoebaDisappearing(x, y);
12576
12577 #if !USE_NEW_AMOEBA_CODE
12578     else if (IS_AMOEBALIVE(element))
12579       AmoebeAbleger(x, y);
12580 #endif
12581
12582     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12583       Life(x, y);
12584     else if (element == EL_EXIT_CLOSED)
12585       CheckExit(x, y);
12586     else if (element == EL_EM_EXIT_CLOSED)
12587       CheckExitEM(x, y);
12588     else if (element == EL_STEEL_EXIT_CLOSED)
12589       CheckExitSteel(x, y);
12590     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12591       CheckExitSteelEM(x, y);
12592     else if (element == EL_SP_EXIT_CLOSED)
12593       CheckExitSP(x, y);
12594     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12595              element == EL_EXPANDABLE_STEELWALL_GROWING)
12596       MauerWaechst(x, y);
12597     else if (element == EL_EXPANDABLE_WALL ||
12598              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12599              element == EL_EXPANDABLE_WALL_VERTICAL ||
12600              element == EL_EXPANDABLE_WALL_ANY ||
12601              element == EL_BD_EXPANDABLE_WALL)
12602       MauerAbleger(x, y);
12603     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12604              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12605              element == EL_EXPANDABLE_STEELWALL_ANY)
12606       MauerAblegerStahl(x, y);
12607     else if (element == EL_FLAMES)
12608       CheckForDragon(x, y);
12609     else if (element == EL_EXPLOSION)
12610       ; /* drawing of correct explosion animation is handled separately */
12611     else if (element == EL_ELEMENT_SNAPPING ||
12612              element == EL_DIAGONAL_SHRINKING ||
12613              element == EL_DIAGONAL_GROWING)
12614     {
12615       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12616
12617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12618     }
12619     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12620       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12621
12622 #endif  // ---------------------------------------------------------------------
12623
12624     if (IS_BELT_ACTIVE(element))
12625       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12626
12627     if (game.magic_wall_active)
12628     {
12629       int jx = local_player->jx, jy = local_player->jy;
12630
12631       /* play the element sound at the position nearest to the player */
12632       if ((element == EL_MAGIC_WALL_FULL ||
12633            element == EL_MAGIC_WALL_ACTIVE ||
12634            element == EL_MAGIC_WALL_EMPTYING ||
12635            element == EL_BD_MAGIC_WALL_FULL ||
12636            element == EL_BD_MAGIC_WALL_ACTIVE ||
12637            element == EL_BD_MAGIC_WALL_EMPTYING ||
12638            element == EL_DC_MAGIC_WALL_FULL ||
12639            element == EL_DC_MAGIC_WALL_ACTIVE ||
12640            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12641           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12642       {
12643         magic_wall_x = x;
12644         magic_wall_y = y;
12645       }
12646     }
12647   }
12648
12649 #if 0
12650   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12651 #endif
12652
12653 #if USE_NEW_AMOEBA_CODE
12654   /* new experimental amoeba growth stuff */
12655   if (!(FrameCounter % 8))
12656   {
12657     static unsigned long random = 1684108901;
12658
12659     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12660     {
12661       x = RND(lev_fieldx);
12662       y = RND(lev_fieldy);
12663       element = Feld[x][y];
12664
12665       if (!IS_PLAYER(x,y) &&
12666           (element == EL_EMPTY ||
12667            CAN_GROW_INTO(element) ||
12668            element == EL_QUICKSAND_EMPTY ||
12669            element == EL_QUICKSAND_FAST_EMPTY ||
12670            element == EL_ACID_SPLASH_LEFT ||
12671            element == EL_ACID_SPLASH_RIGHT))
12672       {
12673         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12674             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12675             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12676             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12677           Feld[x][y] = EL_AMOEBA_DROP;
12678       }
12679
12680       random = random * 129 + 1;
12681     }
12682   }
12683 #endif
12684
12685 #if 0
12686   if (game.explosions_delayed)
12687 #endif
12688   {
12689     game.explosions_delayed = FALSE;
12690
12691     SCAN_PLAYFIELD(x, y)
12692     {
12693       element = Feld[x][y];
12694
12695       if (ExplodeField[x][y])
12696         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12697       else if (element == EL_EXPLOSION)
12698         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12699
12700       ExplodeField[x][y] = EX_TYPE_NONE;
12701     }
12702
12703     game.explosions_delayed = TRUE;
12704   }
12705
12706   if (game.magic_wall_active)
12707   {
12708     if (!(game.magic_wall_time_left % 4))
12709     {
12710       int element = Feld[magic_wall_x][magic_wall_y];
12711
12712       if (element == EL_BD_MAGIC_WALL_FULL ||
12713           element == EL_BD_MAGIC_WALL_ACTIVE ||
12714           element == EL_BD_MAGIC_WALL_EMPTYING)
12715         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12716       else if (element == EL_DC_MAGIC_WALL_FULL ||
12717                element == EL_DC_MAGIC_WALL_ACTIVE ||
12718                element == EL_DC_MAGIC_WALL_EMPTYING)
12719         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12720       else
12721         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12722     }
12723
12724     if (game.magic_wall_time_left > 0)
12725     {
12726       game.magic_wall_time_left--;
12727
12728       if (!game.magic_wall_time_left)
12729       {
12730         SCAN_PLAYFIELD(x, y)
12731         {
12732           element = Feld[x][y];
12733
12734           if (element == EL_MAGIC_WALL_ACTIVE ||
12735               element == EL_MAGIC_WALL_FULL)
12736           {
12737             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12738             TEST_DrawLevelField(x, y);
12739           }
12740           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12741                    element == EL_BD_MAGIC_WALL_FULL)
12742           {
12743             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12744             TEST_DrawLevelField(x, y);
12745           }
12746           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12747                    element == EL_DC_MAGIC_WALL_FULL)
12748           {
12749             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12750             TEST_DrawLevelField(x, y);
12751           }
12752         }
12753
12754         game.magic_wall_active = FALSE;
12755       }
12756     }
12757   }
12758
12759   if (game.light_time_left > 0)
12760   {
12761     game.light_time_left--;
12762
12763     if (game.light_time_left == 0)
12764       RedrawAllLightSwitchesAndInvisibleElements();
12765   }
12766
12767   if (game.timegate_time_left > 0)
12768   {
12769     game.timegate_time_left--;
12770
12771     if (game.timegate_time_left == 0)
12772       CloseAllOpenTimegates();
12773   }
12774
12775   if (game.lenses_time_left > 0)
12776   {
12777     game.lenses_time_left--;
12778
12779     if (game.lenses_time_left == 0)
12780       RedrawAllInvisibleElementsForLenses();
12781   }
12782
12783   if (game.magnify_time_left > 0)
12784   {
12785     game.magnify_time_left--;
12786
12787     if (game.magnify_time_left == 0)
12788       RedrawAllInvisibleElementsForMagnifier();
12789   }
12790
12791   for (i = 0; i < MAX_PLAYERS; i++)
12792   {
12793     struct PlayerInfo *player = &stored_player[i];
12794
12795     if (SHIELD_ON(player))
12796     {
12797       if (player->shield_deadly_time_left)
12798         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12799       else if (player->shield_normal_time_left)
12800         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12801     }
12802   }
12803
12804 #if USE_DELAYED_GFX_REDRAW
12805   SCAN_PLAYFIELD(x, y)
12806   {
12807 #if 1
12808     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12809 #else
12810     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12811         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12812 #endif
12813     {
12814       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12815          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12816
12817       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12818         DrawLevelField(x, y);
12819
12820       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12821         DrawLevelFieldCrumbledSand(x, y);
12822
12823       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12824         DrawLevelFieldCrumbledSandNeighbours(x, y);
12825
12826       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12827         DrawTwinkleOnField(x, y);
12828     }
12829
12830     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12831   }
12832 #endif
12833
12834   CheckLevelTime();
12835
12836   DrawAllPlayers();
12837   PlayAllPlayersSound();
12838
12839   if (options.debug)                    /* calculate frames per second */
12840   {
12841     static unsigned long fps_counter = 0;
12842     static int fps_frames = 0;
12843     unsigned long fps_delay_ms = Counter() - fps_counter;
12844
12845     fps_frames++;
12846
12847     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12848     {
12849       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12850
12851       fps_frames = 0;
12852       fps_counter = Counter();
12853     }
12854
12855     redraw_mask |= REDRAW_FPS;
12856   }
12857
12858   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12859
12860   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12861   {
12862     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12863
12864     local_player->show_envelope = 0;
12865   }
12866
12867 #if 0
12868   debug_print_timestamp(0, "stop main loop profiling ");
12869   printf("----------------------------------------------------------\n");
12870 #endif
12871
12872   /* use random number generator in every frame to make it less predictable */
12873   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12874     RND(1);
12875 }
12876
12877 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12878 {
12879   int min_x = x, min_y = y, max_x = x, max_y = y;
12880   int i;
12881
12882   for (i = 0; i < MAX_PLAYERS; i++)
12883   {
12884     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12885
12886     if (!stored_player[i].active || &stored_player[i] == player)
12887       continue;
12888
12889     min_x = MIN(min_x, jx);
12890     min_y = MIN(min_y, jy);
12891     max_x = MAX(max_x, jx);
12892     max_y = MAX(max_y, jy);
12893   }
12894
12895   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12896 }
12897
12898 static boolean AllPlayersInVisibleScreen()
12899 {
12900   int i;
12901
12902   for (i = 0; i < MAX_PLAYERS; i++)
12903   {
12904     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12905
12906     if (!stored_player[i].active)
12907       continue;
12908
12909     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12910       return FALSE;
12911   }
12912
12913   return TRUE;
12914 }
12915
12916 void ScrollLevel(int dx, int dy)
12917 {
12918 #if 0
12919   /* (directly solved in BlitBitmap() now) */
12920   static Bitmap *bitmap_db_field2 = NULL;
12921   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12922   int x, y;
12923 #else
12924   int x, y;
12925 #endif
12926
12927 #if 0
12928   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12929   /* only horizontal XOR vertical scroll direction allowed */
12930   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12931     return;
12932 #endif
12933
12934 #if 0
12935   /* (directly solved in BlitBitmap() now) */
12936   if (bitmap_db_field2 == NULL)
12937     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12938
12939   /* needed when blitting directly to same bitmap -- should not be needed with
12940      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12941   BlitBitmap(drawto_field, bitmap_db_field2,
12942              FX + TILEX * (dx == -1) - softscroll_offset,
12943              FY + TILEY * (dy == -1) - softscroll_offset,
12944              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12945              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12946              FX + TILEX * (dx == 1) - softscroll_offset,
12947              FY + TILEY * (dy == 1) - softscroll_offset);
12948   BlitBitmap(bitmap_db_field2, drawto_field,
12949              FX + TILEX * (dx == 1) - softscroll_offset,
12950              FY + TILEY * (dy == 1) - softscroll_offset,
12951              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12952              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12953              FX + TILEX * (dx == 1) - softscroll_offset,
12954              FY + TILEY * (dy == 1) - softscroll_offset);
12955
12956 #else
12957
12958 #if 0
12959   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12960   int xsize = (BX2 - BX1 + 1);
12961   int ysize = (BY2 - BY1 + 1);
12962   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12963   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12964   int step  = (start < end ? +1 : -1);
12965
12966   for (i = start; i != end; i += step)
12967   {
12968     BlitBitmap(drawto_field, drawto_field,
12969                FX + TILEX * (dx != 0 ? i + step : 0),
12970                FY + TILEY * (dy != 0 ? i + step : 0),
12971                TILEX * (dx != 0 ? 1 : xsize),
12972                TILEY * (dy != 0 ? 1 : ysize),
12973                FX + TILEX * (dx != 0 ? i : 0),
12974                FY + TILEY * (dy != 0 ? i : 0));
12975   }
12976
12977 #else
12978
12979   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12980
12981   BlitBitmap(drawto_field, drawto_field,
12982              FX + TILEX * (dx == -1) - softscroll_offset,
12983              FY + TILEY * (dy == -1) - softscroll_offset,
12984              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12985              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12986              FX + TILEX * (dx == 1) - softscroll_offset,
12987              FY + TILEY * (dy == 1) - softscroll_offset);
12988 #endif
12989 #endif
12990
12991   if (dx != 0)
12992   {
12993     x = (dx == 1 ? BX1 : BX2);
12994     for (y = BY1; y <= BY2; y++)
12995       DrawScreenField(x, y);
12996   }
12997
12998   if (dy != 0)
12999   {
13000     y = (dy == 1 ? BY1 : BY2);
13001     for (x = BX1; x <= BX2; x++)
13002       DrawScreenField(x, y);
13003   }
13004
13005   redraw_mask |= REDRAW_FIELD;
13006 }
13007
13008 static boolean canFallDown(struct PlayerInfo *player)
13009 {
13010   int jx = player->jx, jy = player->jy;
13011
13012   return (IN_LEV_FIELD(jx, jy + 1) &&
13013           (IS_FREE(jx, jy + 1) ||
13014            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13015           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13016           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13017 }
13018
13019 static boolean canPassField(int x, int y, int move_dir)
13020 {
13021   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13022   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13023   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13024   int nextx = x + dx;
13025   int nexty = y + dy;
13026   int element = Feld[x][y];
13027
13028   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13029           !CAN_MOVE(element) &&
13030           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13031           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13032           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13033 }
13034
13035 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13036 {
13037   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13038   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13039   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13040   int newx = x + dx;
13041   int newy = y + dy;
13042
13043   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13044           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13045           (IS_DIGGABLE(Feld[newx][newy]) ||
13046            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13047            canPassField(newx, newy, move_dir)));
13048 }
13049
13050 static void CheckGravityMovement(struct PlayerInfo *player)
13051 {
13052 #if USE_PLAYER_GRAVITY
13053   if (player->gravity && !player->programmed_action)
13054 #else
13055   if (game.gravity && !player->programmed_action)
13056 #endif
13057   {
13058     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13059     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13060     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13061     int jx = player->jx, jy = player->jy;
13062     boolean player_is_moving_to_valid_field =
13063       (!player_is_snapping &&
13064        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13065         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13066     boolean player_can_fall_down = canFallDown(player);
13067
13068     if (player_can_fall_down &&
13069         !player_is_moving_to_valid_field)
13070       player->programmed_action = MV_DOWN;
13071   }
13072 }
13073
13074 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13075 {
13076   return CheckGravityMovement(player);
13077
13078 #if USE_PLAYER_GRAVITY
13079   if (player->gravity && !player->programmed_action)
13080 #else
13081   if (game.gravity && !player->programmed_action)
13082 #endif
13083   {
13084     int jx = player->jx, jy = player->jy;
13085     boolean field_under_player_is_free =
13086       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13087     boolean player_is_standing_on_valid_field =
13088       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13089        (IS_WALKABLE(Feld[jx][jy]) &&
13090         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13091
13092     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13093       player->programmed_action = MV_DOWN;
13094   }
13095 }
13096
13097 /*
13098   MovePlayerOneStep()
13099   -----------------------------------------------------------------------------
13100   dx, dy:               direction (non-diagonal) to try to move the player to
13101   real_dx, real_dy:     direction as read from input device (can be diagonal)
13102 */
13103
13104 boolean MovePlayerOneStep(struct PlayerInfo *player,
13105                           int dx, int dy, int real_dx, int real_dy)
13106 {
13107   int jx = player->jx, jy = player->jy;
13108   int new_jx = jx + dx, new_jy = jy + dy;
13109 #if !USE_FIXED_DONT_RUN_INTO
13110   int element;
13111 #endif
13112   int can_move;
13113   boolean player_can_move = !player->cannot_move;
13114
13115   if (!player->active || (!dx && !dy))
13116     return MP_NO_ACTION;
13117
13118   player->MovDir = (dx < 0 ? MV_LEFT :
13119                     dx > 0 ? MV_RIGHT :
13120                     dy < 0 ? MV_UP :
13121                     dy > 0 ? MV_DOWN :  MV_NONE);
13122
13123   if (!IN_LEV_FIELD(new_jx, new_jy))
13124     return MP_NO_ACTION;
13125
13126   if (!player_can_move)
13127   {
13128     if (player->MovPos == 0)
13129     {
13130       player->is_moving = FALSE;
13131       player->is_digging = FALSE;
13132       player->is_collecting = FALSE;
13133       player->is_snapping = FALSE;
13134       player->is_pushing = FALSE;
13135     }
13136   }
13137
13138 #if 1
13139   if (!options.network && game.centered_player_nr == -1 &&
13140       !AllPlayersInSight(player, new_jx, new_jy))
13141     return MP_NO_ACTION;
13142 #else
13143   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13144     return MP_NO_ACTION;
13145 #endif
13146
13147 #if !USE_FIXED_DONT_RUN_INTO
13148   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13149
13150   /* (moved to DigField()) */
13151   if (player_can_move && DONT_RUN_INTO(element))
13152   {
13153     if (element == EL_ACID && dx == 0 && dy == 1)
13154     {
13155       SplashAcid(new_jx, new_jy);
13156       Feld[jx][jy] = EL_PLAYER_1;
13157       InitMovingField(jx, jy, MV_DOWN);
13158       Store[jx][jy] = EL_ACID;
13159       ContinueMoving(jx, jy);
13160       BuryPlayer(player);
13161     }
13162     else
13163       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13164
13165     return MP_MOVING;
13166   }
13167 #endif
13168
13169   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13170   if (can_move != MP_MOVING)
13171     return can_move;
13172
13173   /* check if DigField() has caused relocation of the player */
13174   if (player->jx != jx || player->jy != jy)
13175     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13176
13177   StorePlayer[jx][jy] = 0;
13178   player->last_jx = jx;
13179   player->last_jy = jy;
13180   player->jx = new_jx;
13181   player->jy = new_jy;
13182   StorePlayer[new_jx][new_jy] = player->element_nr;
13183
13184   if (player->move_delay_value_next != -1)
13185   {
13186     player->move_delay_value = player->move_delay_value_next;
13187     player->move_delay_value_next = -1;
13188   }
13189
13190   player->MovPos =
13191     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13192
13193   player->step_counter++;
13194
13195   PlayerVisit[jx][jy] = FrameCounter;
13196
13197 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13198   player->is_moving = TRUE;
13199 #endif
13200
13201 #if 1
13202   /* should better be called in MovePlayer(), but this breaks some tapes */
13203   ScrollPlayer(player, SCROLL_INIT);
13204 #endif
13205
13206   return MP_MOVING;
13207 }
13208
13209 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13210 {
13211   int jx = player->jx, jy = player->jy;
13212   int old_jx = jx, old_jy = jy;
13213   int moved = MP_NO_ACTION;
13214
13215   if (!player->active)
13216     return FALSE;
13217
13218   if (!dx && !dy)
13219   {
13220     if (player->MovPos == 0)
13221     {
13222       player->is_moving = FALSE;
13223       player->is_digging = FALSE;
13224       player->is_collecting = FALSE;
13225       player->is_snapping = FALSE;
13226       player->is_pushing = FALSE;
13227     }
13228
13229     return FALSE;
13230   }
13231
13232   if (player->move_delay > 0)
13233     return FALSE;
13234
13235   player->move_delay = -1;              /* set to "uninitialized" value */
13236
13237   /* store if player is automatically moved to next field */
13238   player->is_auto_moving = (player->programmed_action != MV_NONE);
13239
13240   /* remove the last programmed player action */
13241   player->programmed_action = 0;
13242
13243   if (player->MovPos)
13244   {
13245     /* should only happen if pre-1.2 tape recordings are played */
13246     /* this is only for backward compatibility */
13247
13248     int original_move_delay_value = player->move_delay_value;
13249
13250 #if DEBUG
13251     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13252            tape.counter);
13253 #endif
13254
13255     /* scroll remaining steps with finest movement resolution */
13256     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13257
13258     while (player->MovPos)
13259     {
13260       ScrollPlayer(player, SCROLL_GO_ON);
13261       ScrollScreen(NULL, SCROLL_GO_ON);
13262
13263       AdvanceFrameAndPlayerCounters(player->index_nr);
13264
13265       DrawAllPlayers();
13266       BackToFront();
13267     }
13268
13269     player->move_delay_value = original_move_delay_value;
13270   }
13271
13272   player->is_active = FALSE;
13273
13274   if (player->last_move_dir & MV_HORIZONTAL)
13275   {
13276     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13277       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13278   }
13279   else
13280   {
13281     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13282       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13283   }
13284
13285 #if USE_FIXED_BORDER_RUNNING_GFX
13286   if (!moved && !player->is_active)
13287   {
13288     player->is_moving = FALSE;
13289     player->is_digging = FALSE;
13290     player->is_collecting = FALSE;
13291     player->is_snapping = FALSE;
13292     player->is_pushing = FALSE;
13293   }
13294 #endif
13295
13296   jx = player->jx;
13297   jy = player->jy;
13298
13299 #if 1
13300   if (moved & MP_MOVING && !ScreenMovPos &&
13301       (player->index_nr == game.centered_player_nr ||
13302        game.centered_player_nr == -1))
13303 #else
13304   if (moved & MP_MOVING && !ScreenMovPos &&
13305       (player == local_player || !options.network))
13306 #endif
13307   {
13308     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13309     int offset = game.scroll_delay_value;
13310
13311     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13312     {
13313       /* actual player has left the screen -- scroll in that direction */
13314       if (jx != old_jx)         /* player has moved horizontally */
13315         scroll_x += (jx - old_jx);
13316       else                      /* player has moved vertically */
13317         scroll_y += (jy - old_jy);
13318     }
13319     else
13320     {
13321       if (jx != old_jx)         /* player has moved horizontally */
13322       {
13323         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13324             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13325           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13326
13327         /* don't scroll over playfield boundaries */
13328         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13329           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13330
13331         /* don't scroll more than one field at a time */
13332         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13333
13334         /* don't scroll against the player's moving direction */
13335         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13336             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13337           scroll_x = old_scroll_x;
13338       }
13339       else                      /* player has moved vertically */
13340       {
13341         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13342             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13343           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13344
13345         /* don't scroll over playfield boundaries */
13346         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13347           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13348
13349         /* don't scroll more than one field at a time */
13350         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13351
13352         /* don't scroll against the player's moving direction */
13353         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13354             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13355           scroll_y = old_scroll_y;
13356       }
13357     }
13358
13359     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13360     {
13361 #if 1
13362       if (!options.network && game.centered_player_nr == -1 &&
13363           !AllPlayersInVisibleScreen())
13364       {
13365         scroll_x = old_scroll_x;
13366         scroll_y = old_scroll_y;
13367       }
13368       else
13369 #else
13370       if (!options.network && !AllPlayersInVisibleScreen())
13371       {
13372         scroll_x = old_scroll_x;
13373         scroll_y = old_scroll_y;
13374       }
13375       else
13376 #endif
13377       {
13378         ScrollScreen(player, SCROLL_INIT);
13379         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13380       }
13381     }
13382   }
13383
13384   player->StepFrame = 0;
13385
13386   if (moved & MP_MOVING)
13387   {
13388     if (old_jx != jx && old_jy == jy)
13389       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13390     else if (old_jx == jx && old_jy != jy)
13391       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13392
13393     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13394
13395     player->last_move_dir = player->MovDir;
13396     player->is_moving = TRUE;
13397     player->is_snapping = FALSE;
13398     player->is_switching = FALSE;
13399     player->is_dropping = FALSE;
13400     player->is_dropping_pressed = FALSE;
13401     player->drop_pressed_delay = 0;
13402
13403 #if 0
13404     /* should better be called here than above, but this breaks some tapes */
13405     ScrollPlayer(player, SCROLL_INIT);
13406 #endif
13407   }
13408   else
13409   {
13410     CheckGravityMovementWhenNotMoving(player);
13411
13412     player->is_moving = FALSE;
13413
13414     /* at this point, the player is allowed to move, but cannot move right now
13415        (e.g. because of something blocking the way) -- ensure that the player
13416        is also allowed to move in the next frame (in old versions before 3.1.1,
13417        the player was forced to wait again for eight frames before next try) */
13418
13419     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13420       player->move_delay = 0;   /* allow direct movement in the next frame */
13421   }
13422
13423   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13424     player->move_delay = player->move_delay_value;
13425
13426   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13427   {
13428     TestIfPlayerTouchesBadThing(jx, jy);
13429     TestIfPlayerTouchesCustomElement(jx, jy);
13430   }
13431
13432   if (!player->active)
13433     RemovePlayer(player);
13434
13435   return moved;
13436 }
13437
13438 void ScrollPlayer(struct PlayerInfo *player, int mode)
13439 {
13440   int jx = player->jx, jy = player->jy;
13441   int last_jx = player->last_jx, last_jy = player->last_jy;
13442   int move_stepsize = TILEX / player->move_delay_value;
13443
13444 #if USE_NEW_PLAYER_SPEED
13445   if (!player->active)
13446     return;
13447
13448   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13449     return;
13450 #else
13451   if (!player->active || player->MovPos == 0)
13452     return;
13453 #endif
13454
13455   if (mode == SCROLL_INIT)
13456   {
13457     player->actual_frame_counter = FrameCounter;
13458     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13459
13460     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13461         Feld[last_jx][last_jy] == EL_EMPTY)
13462     {
13463       int last_field_block_delay = 0;   /* start with no blocking at all */
13464       int block_delay_adjustment = player->block_delay_adjustment;
13465
13466       /* if player blocks last field, add delay for exactly one move */
13467       if (player->block_last_field)
13468       {
13469         last_field_block_delay += player->move_delay_value;
13470
13471         /* when blocking enabled, prevent moving up despite gravity */
13472 #if USE_PLAYER_GRAVITY
13473         if (player->gravity && player->MovDir == MV_UP)
13474           block_delay_adjustment = -1;
13475 #else
13476         if (game.gravity && player->MovDir == MV_UP)
13477           block_delay_adjustment = -1;
13478 #endif
13479       }
13480
13481       /* add block delay adjustment (also possible when not blocking) */
13482       last_field_block_delay += block_delay_adjustment;
13483
13484       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13485       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13486     }
13487
13488 #if USE_NEW_PLAYER_SPEED
13489     if (player->MovPos != 0)    /* player has not yet reached destination */
13490       return;
13491 #else
13492     return;
13493 #endif
13494   }
13495   else if (!FrameReached(&player->actual_frame_counter, 1))
13496     return;
13497
13498 #if USE_NEW_PLAYER_SPEED
13499   if (player->MovPos != 0)
13500   {
13501     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13502     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13503
13504     /* before DrawPlayer() to draw correct player graphic for this case */
13505     if (player->MovPos == 0)
13506       CheckGravityMovement(player);
13507   }
13508 #else
13509   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13510   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13511
13512   /* before DrawPlayer() to draw correct player graphic for this case */
13513   if (player->MovPos == 0)
13514     CheckGravityMovement(player);
13515 #endif
13516
13517   if (player->MovPos == 0)      /* player reached destination field */
13518   {
13519     if (player->move_delay_reset_counter > 0)
13520     {
13521       player->move_delay_reset_counter--;
13522
13523       if (player->move_delay_reset_counter == 0)
13524       {
13525         /* continue with normal speed after quickly moving through gate */
13526         HALVE_PLAYER_SPEED(player);
13527
13528         /* be able to make the next move without delay */
13529         player->move_delay = 0;
13530       }
13531     }
13532
13533     player->last_jx = jx;
13534     player->last_jy = jy;
13535
13536     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13537         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13538         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13539         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13540         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13541         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13542     {
13543       DrawPlayer(player);       /* needed here only to cleanup last field */
13544       RemovePlayer(player);
13545
13546       if (local_player->friends_still_needed == 0 ||
13547           IS_SP_ELEMENT(Feld[jx][jy]))
13548         PlayerWins(player);
13549     }
13550
13551     /* this breaks one level: "machine", level 000 */
13552     {
13553       int move_direction = player->MovDir;
13554       int enter_side = MV_DIR_OPPOSITE(move_direction);
13555       int leave_side = move_direction;
13556       int old_jx = last_jx;
13557       int old_jy = last_jy;
13558       int old_element = Feld[old_jx][old_jy];
13559       int new_element = Feld[jx][jy];
13560
13561       if (IS_CUSTOM_ELEMENT(old_element))
13562         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13563                                    CE_LEFT_BY_PLAYER,
13564                                    player->index_bit, leave_side);
13565
13566       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13567                                           CE_PLAYER_LEAVES_X,
13568                                           player->index_bit, leave_side);
13569
13570       if (IS_CUSTOM_ELEMENT(new_element))
13571         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13572                                    player->index_bit, enter_side);
13573
13574       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13575                                           CE_PLAYER_ENTERS_X,
13576                                           player->index_bit, enter_side);
13577
13578 #if USE_FIX_CE_ACTION_WITH_PLAYER
13579       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13580                                         CE_MOVE_OF_X, move_direction);
13581 #else
13582       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13583                                         CE_MOVE_OF_X, move_direction);
13584 #endif
13585     }
13586
13587     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13588     {
13589       TestIfPlayerTouchesBadThing(jx, jy);
13590       TestIfPlayerTouchesCustomElement(jx, jy);
13591
13592       /* needed because pushed element has not yet reached its destination,
13593          so it would trigger a change event at its previous field location */
13594       if (!player->is_pushing)
13595         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13596
13597       if (!player->active)
13598         RemovePlayer(player);
13599     }
13600
13601     if (!local_player->LevelSolved && level.use_step_counter)
13602     {
13603       int i;
13604
13605       TimePlayed++;
13606
13607       if (TimeLeft > 0)
13608       {
13609         TimeLeft--;
13610
13611         if (TimeLeft <= 10 && setup.time_limit)
13612           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13613
13614 #if 1
13615         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13616
13617         DisplayGameControlValues();
13618 #else
13619         DrawGameValue_Time(TimeLeft);
13620 #endif
13621
13622         if (!TimeLeft && setup.time_limit)
13623           for (i = 0; i < MAX_PLAYERS; i++)
13624             KillPlayer(&stored_player[i]);
13625       }
13626 #if 1
13627       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13628       {
13629         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13630
13631         DisplayGameControlValues();
13632       }
13633 #else
13634       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13635         DrawGameValue_Time(TimePlayed);
13636 #endif
13637     }
13638
13639     if (tape.single_step && tape.recording && !tape.pausing &&
13640         !player->programmed_action)
13641       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13642   }
13643 }
13644
13645 void ScrollScreen(struct PlayerInfo *player, int mode)
13646 {
13647   static unsigned long screen_frame_counter = 0;
13648
13649   if (mode == SCROLL_INIT)
13650   {
13651     /* set scrolling step size according to actual player's moving speed */
13652     ScrollStepSize = TILEX / player->move_delay_value;
13653
13654     screen_frame_counter = FrameCounter;
13655     ScreenMovDir = player->MovDir;
13656     ScreenMovPos = player->MovPos;
13657     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13658     return;
13659   }
13660   else if (!FrameReached(&screen_frame_counter, 1))
13661     return;
13662
13663   if (ScreenMovPos)
13664   {
13665     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13666     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13667     redraw_mask |= REDRAW_FIELD;
13668   }
13669   else
13670     ScreenMovDir = MV_NONE;
13671 }
13672
13673 void TestIfPlayerTouchesCustomElement(int x, int y)
13674 {
13675   static int xy[4][2] =
13676   {
13677     { 0, -1 },
13678     { -1, 0 },
13679     { +1, 0 },
13680     { 0, +1 }
13681   };
13682   static int trigger_sides[4][2] =
13683   {
13684     /* center side       border side */
13685     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13686     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13687     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13688     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13689   };
13690   static int touch_dir[4] =
13691   {
13692     MV_LEFT | MV_RIGHT,
13693     MV_UP   | MV_DOWN,
13694     MV_UP   | MV_DOWN,
13695     MV_LEFT | MV_RIGHT
13696   };
13697   int center_element = Feld[x][y];      /* should always be non-moving! */
13698   int i;
13699
13700   for (i = 0; i < NUM_DIRECTIONS; i++)
13701   {
13702     int xx = x + xy[i][0];
13703     int yy = y + xy[i][1];
13704     int center_side = trigger_sides[i][0];
13705     int border_side = trigger_sides[i][1];
13706     int border_element;
13707
13708     if (!IN_LEV_FIELD(xx, yy))
13709       continue;
13710
13711     if (IS_PLAYER(x, y))                /* player found at center element */
13712     {
13713       struct PlayerInfo *player = PLAYERINFO(x, y);
13714
13715       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13716         border_element = Feld[xx][yy];          /* may be moving! */
13717       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13718         border_element = Feld[xx][yy];
13719       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13720         border_element = MovingOrBlocked2Element(xx, yy);
13721       else
13722         continue;               /* center and border element do not touch */
13723
13724       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13725                                  player->index_bit, border_side);
13726       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13727                                           CE_PLAYER_TOUCHES_X,
13728                                           player->index_bit, border_side);
13729
13730 #if USE_FIX_CE_ACTION_WITH_PLAYER
13731       {
13732         /* use player element that is initially defined in the level playfield,
13733            not the player element that corresponds to the runtime player number
13734            (example: a level that contains EL_PLAYER_3 as the only player would
13735            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13736         int player_element = PLAYERINFO(x, y)->initial_element;
13737
13738         CheckElementChangeBySide(xx, yy, border_element, player_element,
13739                                  CE_TOUCHING_X, border_side);
13740       }
13741 #endif
13742     }
13743     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13744     {
13745       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13746
13747       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13748       {
13749         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13750           continue;             /* center and border element do not touch */
13751       }
13752
13753       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13754                                  player->index_bit, center_side);
13755       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13756                                           CE_PLAYER_TOUCHES_X,
13757                                           player->index_bit, center_side);
13758
13759 #if USE_FIX_CE_ACTION_WITH_PLAYER
13760       {
13761         /* use player element that is initially defined in the level playfield,
13762            not the player element that corresponds to the runtime player number
13763            (example: a level that contains EL_PLAYER_3 as the only player would
13764            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13765         int player_element = PLAYERINFO(xx, yy)->initial_element;
13766
13767         CheckElementChangeBySide(x, y, center_element, player_element,
13768                                  CE_TOUCHING_X, center_side);
13769       }
13770 #endif
13771
13772       break;
13773     }
13774   }
13775 }
13776
13777 #if USE_ELEMENT_TOUCHING_BUGFIX
13778
13779 void TestIfElementTouchesCustomElement(int x, int y)
13780 {
13781   static int xy[4][2] =
13782   {
13783     { 0, -1 },
13784     { -1, 0 },
13785     { +1, 0 },
13786     { 0, +1 }
13787   };
13788   static int trigger_sides[4][2] =
13789   {
13790     /* center side      border side */
13791     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13792     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13793     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13794     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13795   };
13796   static int touch_dir[4] =
13797   {
13798     MV_LEFT | MV_RIGHT,
13799     MV_UP   | MV_DOWN,
13800     MV_UP   | MV_DOWN,
13801     MV_LEFT | MV_RIGHT
13802   };
13803   boolean change_center_element = FALSE;
13804   int center_element = Feld[x][y];      /* should always be non-moving! */
13805   int border_element_old[NUM_DIRECTIONS];
13806   int i;
13807
13808   for (i = 0; i < NUM_DIRECTIONS; i++)
13809   {
13810     int xx = x + xy[i][0];
13811     int yy = y + xy[i][1];
13812     int border_element;
13813
13814     border_element_old[i] = -1;
13815
13816     if (!IN_LEV_FIELD(xx, yy))
13817       continue;
13818
13819     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13820       border_element = Feld[xx][yy];    /* may be moving! */
13821     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13822       border_element = Feld[xx][yy];
13823     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13824       border_element = MovingOrBlocked2Element(xx, yy);
13825     else
13826       continue;                 /* center and border element do not touch */
13827
13828     border_element_old[i] = border_element;
13829   }
13830
13831   for (i = 0; i < NUM_DIRECTIONS; i++)
13832   {
13833     int xx = x + xy[i][0];
13834     int yy = y + xy[i][1];
13835     int center_side = trigger_sides[i][0];
13836     int border_element = border_element_old[i];
13837
13838     if (border_element == -1)
13839       continue;
13840
13841     /* check for change of border element */
13842     CheckElementChangeBySide(xx, yy, border_element, center_element,
13843                              CE_TOUCHING_X, center_side);
13844
13845     /* (center element cannot be player, so we dont have to check this here) */
13846   }
13847
13848   for (i = 0; i < NUM_DIRECTIONS; i++)
13849   {
13850     int xx = x + xy[i][0];
13851     int yy = y + xy[i][1];
13852     int border_side = trigger_sides[i][1];
13853     int border_element = border_element_old[i];
13854
13855     if (border_element == -1)
13856       continue;
13857
13858     /* check for change of center element (but change it only once) */
13859     if (!change_center_element)
13860       change_center_element =
13861         CheckElementChangeBySide(x, y, center_element, border_element,
13862                                  CE_TOUCHING_X, border_side);
13863
13864 #if USE_FIX_CE_ACTION_WITH_PLAYER
13865     if (IS_PLAYER(xx, yy))
13866     {
13867       /* use player element that is initially defined in the level playfield,
13868          not the player element that corresponds to the runtime player number
13869          (example: a level that contains EL_PLAYER_3 as the only player would
13870          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13871       int player_element = PLAYERINFO(xx, yy)->initial_element;
13872
13873       CheckElementChangeBySide(x, y, center_element, player_element,
13874                                CE_TOUCHING_X, border_side);
13875     }
13876 #endif
13877   }
13878 }
13879
13880 #else
13881
13882 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13883 {
13884   static int xy[4][2] =
13885   {
13886     { 0, -1 },
13887     { -1, 0 },
13888     { +1, 0 },
13889     { 0, +1 }
13890   };
13891   static int trigger_sides[4][2] =
13892   {
13893     /* center side      border side */
13894     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13895     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13896     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13897     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13898   };
13899   static int touch_dir[4] =
13900   {
13901     MV_LEFT | MV_RIGHT,
13902     MV_UP   | MV_DOWN,
13903     MV_UP   | MV_DOWN,
13904     MV_LEFT | MV_RIGHT
13905   };
13906   boolean change_center_element = FALSE;
13907   int center_element = Feld[x][y];      /* should always be non-moving! */
13908   int i;
13909
13910   for (i = 0; i < NUM_DIRECTIONS; i++)
13911   {
13912     int xx = x + xy[i][0];
13913     int yy = y + xy[i][1];
13914     int center_side = trigger_sides[i][0];
13915     int border_side = trigger_sides[i][1];
13916     int border_element;
13917
13918     if (!IN_LEV_FIELD(xx, yy))
13919       continue;
13920
13921     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13922       border_element = Feld[xx][yy];    /* may be moving! */
13923     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13924       border_element = Feld[xx][yy];
13925     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13926       border_element = MovingOrBlocked2Element(xx, yy);
13927     else
13928       continue;                 /* center and border element do not touch */
13929
13930     /* check for change of center element (but change it only once) */
13931     if (!change_center_element)
13932       change_center_element =
13933         CheckElementChangeBySide(x, y, center_element, border_element,
13934                                  CE_TOUCHING_X, border_side);
13935
13936     /* check for change of border element */
13937     CheckElementChangeBySide(xx, yy, border_element, center_element,
13938                              CE_TOUCHING_X, center_side);
13939   }
13940 }
13941
13942 #endif
13943
13944 void TestIfElementHitsCustomElement(int x, int y, int direction)
13945 {
13946   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13947   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13948   int hitx = x + dx, hity = y + dy;
13949   int hitting_element = Feld[x][y];
13950   int touched_element;
13951
13952   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13953     return;
13954
13955   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13956                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13957
13958   if (IN_LEV_FIELD(hitx, hity))
13959   {
13960     int opposite_direction = MV_DIR_OPPOSITE(direction);
13961     int hitting_side = direction;
13962     int touched_side = opposite_direction;
13963     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13964                           MovDir[hitx][hity] != direction ||
13965                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13966
13967     object_hit = TRUE;
13968
13969     if (object_hit)
13970     {
13971       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13972                                CE_HITTING_X, touched_side);
13973
13974       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13975                                CE_HIT_BY_X, hitting_side);
13976
13977       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13978                                CE_HIT_BY_SOMETHING, opposite_direction);
13979
13980 #if USE_FIX_CE_ACTION_WITH_PLAYER
13981       if (IS_PLAYER(hitx, hity))
13982       {
13983         /* use player element that is initially defined in the level playfield,
13984            not the player element that corresponds to the runtime player number
13985            (example: a level that contains EL_PLAYER_3 as the only player would
13986            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13987         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13988
13989         CheckElementChangeBySide(x, y, hitting_element, player_element,
13990                                  CE_HITTING_X, touched_side);
13991       }
13992 #endif
13993     }
13994   }
13995
13996   /* "hitting something" is also true when hitting the playfield border */
13997   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13998                            CE_HITTING_SOMETHING, direction);
13999 }
14000
14001 #if 0
14002 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14003 {
14004   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14005   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14006   int hitx = x + dx, hity = y + dy;
14007   int hitting_element = Feld[x][y];
14008   int touched_element;
14009 #if 0
14010   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14011                         !IS_FREE(hitx, hity) &&
14012                         (!IS_MOVING(hitx, hity) ||
14013                          MovDir[hitx][hity] != direction ||
14014                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14015 #endif
14016
14017   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14018     return;
14019
14020 #if 0
14021   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14022     return;
14023 #endif
14024
14025   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14026                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14027
14028   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14029                            EP_CAN_SMASH_EVERYTHING, direction);
14030
14031   if (IN_LEV_FIELD(hitx, hity))
14032   {
14033     int opposite_direction = MV_DIR_OPPOSITE(direction);
14034     int hitting_side = direction;
14035     int touched_side = opposite_direction;
14036 #if 0
14037     int touched_element = MovingOrBlocked2Element(hitx, hity);
14038 #endif
14039 #if 1
14040     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14041                           MovDir[hitx][hity] != direction ||
14042                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14043
14044     object_hit = TRUE;
14045 #endif
14046
14047     if (object_hit)
14048     {
14049       int i;
14050
14051       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14052                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14053
14054       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14055                                CE_OTHER_IS_SMASHING, touched_side);
14056
14057       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14058                                CE_OTHER_GETS_SMASHED, hitting_side);
14059     }
14060   }
14061 }
14062 #endif
14063
14064 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14065 {
14066   int i, kill_x = -1, kill_y = -1;
14067
14068   int bad_element = -1;
14069   static int test_xy[4][2] =
14070   {
14071     { 0, -1 },
14072     { -1, 0 },
14073     { +1, 0 },
14074     { 0, +1 }
14075   };
14076   static int test_dir[4] =
14077   {
14078     MV_UP,
14079     MV_LEFT,
14080     MV_RIGHT,
14081     MV_DOWN
14082   };
14083
14084   for (i = 0; i < NUM_DIRECTIONS; i++)
14085   {
14086     int test_x, test_y, test_move_dir, test_element;
14087
14088     test_x = good_x + test_xy[i][0];
14089     test_y = good_y + test_xy[i][1];
14090
14091     if (!IN_LEV_FIELD(test_x, test_y))
14092       continue;
14093
14094     test_move_dir =
14095       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14096
14097     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14098
14099     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14100        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14101     */
14102     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14103         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14104     {
14105       kill_x = test_x;
14106       kill_y = test_y;
14107       bad_element = test_element;
14108
14109       break;
14110     }
14111   }
14112
14113   if (kill_x != -1 || kill_y != -1)
14114   {
14115     if (IS_PLAYER(good_x, good_y))
14116     {
14117       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14118
14119       if (player->shield_deadly_time_left > 0 &&
14120           !IS_INDESTRUCTIBLE(bad_element))
14121         Bang(kill_x, kill_y);
14122       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14123         KillPlayer(player);
14124     }
14125     else
14126       Bang(good_x, good_y);
14127   }
14128 }
14129
14130 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14131 {
14132   int i, kill_x = -1, kill_y = -1;
14133   int bad_element = Feld[bad_x][bad_y];
14134   static int test_xy[4][2] =
14135   {
14136     { 0, -1 },
14137     { -1, 0 },
14138     { +1, 0 },
14139     { 0, +1 }
14140   };
14141   static int touch_dir[4] =
14142   {
14143     MV_LEFT | MV_RIGHT,
14144     MV_UP   | MV_DOWN,
14145     MV_UP   | MV_DOWN,
14146     MV_LEFT | MV_RIGHT
14147   };
14148   static int test_dir[4] =
14149   {
14150     MV_UP,
14151     MV_LEFT,
14152     MV_RIGHT,
14153     MV_DOWN
14154   };
14155
14156   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14157     return;
14158
14159   for (i = 0; i < NUM_DIRECTIONS; i++)
14160   {
14161     int test_x, test_y, test_move_dir, test_element;
14162
14163     test_x = bad_x + test_xy[i][0];
14164     test_y = bad_y + test_xy[i][1];
14165
14166     if (!IN_LEV_FIELD(test_x, test_y))
14167       continue;
14168
14169     test_move_dir =
14170       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14171
14172     test_element = Feld[test_x][test_y];
14173
14174     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14175        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14176     */
14177     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14178         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14179     {
14180       /* good thing is player or penguin that does not move away */
14181       if (IS_PLAYER(test_x, test_y))
14182       {
14183         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14184
14185         if (bad_element == EL_ROBOT && player->is_moving)
14186           continue;     /* robot does not kill player if he is moving */
14187
14188         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14189         {
14190           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14191             continue;           /* center and border element do not touch */
14192         }
14193
14194         kill_x = test_x;
14195         kill_y = test_y;
14196
14197         break;
14198       }
14199       else if (test_element == EL_PENGUIN)
14200       {
14201         kill_x = test_x;
14202         kill_y = test_y;
14203
14204         break;
14205       }
14206     }
14207   }
14208
14209   if (kill_x != -1 || kill_y != -1)
14210   {
14211     if (IS_PLAYER(kill_x, kill_y))
14212     {
14213       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14214
14215       if (player->shield_deadly_time_left > 0 &&
14216           !IS_INDESTRUCTIBLE(bad_element))
14217         Bang(bad_x, bad_y);
14218       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14219         KillPlayer(player);
14220     }
14221     else
14222       Bang(kill_x, kill_y);
14223   }
14224 }
14225
14226 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14227 {
14228   int bad_element = Feld[bad_x][bad_y];
14229   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14230   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14231   int test_x = bad_x + dx, test_y = bad_y + dy;
14232   int test_move_dir, test_element;
14233   int kill_x = -1, kill_y = -1;
14234
14235   if (!IN_LEV_FIELD(test_x, test_y))
14236     return;
14237
14238   test_move_dir =
14239     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14240
14241   test_element = Feld[test_x][test_y];
14242
14243   if (test_move_dir != bad_move_dir)
14244   {
14245     /* good thing can be player or penguin that does not move away */
14246     if (IS_PLAYER(test_x, test_y))
14247     {
14248       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14249
14250       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14251          player as being hit when he is moving towards the bad thing, because
14252          the "get hit by" condition would be lost after the player stops) */
14253       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14254         return;         /* player moves away from bad thing */
14255
14256       kill_x = test_x;
14257       kill_y = test_y;
14258     }
14259     else if (test_element == EL_PENGUIN)
14260     {
14261       kill_x = test_x;
14262       kill_y = test_y;
14263     }
14264   }
14265
14266   if (kill_x != -1 || kill_y != -1)
14267   {
14268     if (IS_PLAYER(kill_x, kill_y))
14269     {
14270       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14271
14272       if (player->shield_deadly_time_left > 0 &&
14273           !IS_INDESTRUCTIBLE(bad_element))
14274         Bang(bad_x, bad_y);
14275       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14276         KillPlayer(player);
14277     }
14278     else
14279       Bang(kill_x, kill_y);
14280   }
14281 }
14282
14283 void TestIfPlayerTouchesBadThing(int x, int y)
14284 {
14285   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14286 }
14287
14288 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14289 {
14290   TestIfGoodThingHitsBadThing(x, y, move_dir);
14291 }
14292
14293 void TestIfBadThingTouchesPlayer(int x, int y)
14294 {
14295   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14296 }
14297
14298 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14299 {
14300   TestIfBadThingHitsGoodThing(x, y, move_dir);
14301 }
14302
14303 void TestIfFriendTouchesBadThing(int x, int y)
14304 {
14305   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14306 }
14307
14308 void TestIfBadThingTouchesFriend(int x, int y)
14309 {
14310   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14311 }
14312
14313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14314 {
14315   int i, kill_x = bad_x, kill_y = bad_y;
14316   static int xy[4][2] =
14317   {
14318     { 0, -1 },
14319     { -1, 0 },
14320     { +1, 0 },
14321     { 0, +1 }
14322   };
14323
14324   for (i = 0; i < NUM_DIRECTIONS; i++)
14325   {
14326     int x, y, element;
14327
14328     x = bad_x + xy[i][0];
14329     y = bad_y + xy[i][1];
14330     if (!IN_LEV_FIELD(x, y))
14331       continue;
14332
14333     element = Feld[x][y];
14334     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14335         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14336     {
14337       kill_x = x;
14338       kill_y = y;
14339       break;
14340     }
14341   }
14342
14343   if (kill_x != bad_x || kill_y != bad_y)
14344     Bang(bad_x, bad_y);
14345 }
14346
14347 void KillPlayer(struct PlayerInfo *player)
14348 {
14349   int jx = player->jx, jy = player->jy;
14350
14351   if (!player->active)
14352     return;
14353
14354 #if 0
14355   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14356          player->killed, player->active, player->reanimated);
14357 #endif
14358
14359   /* the following code was introduced to prevent an infinite loop when calling
14360      -> Bang()
14361      -> CheckTriggeredElementChangeExt()
14362      -> ExecuteCustomElementAction()
14363      -> KillPlayer()
14364      -> (infinitely repeating the above sequence of function calls)
14365      which occurs when killing the player while having a CE with the setting
14366      "kill player X when explosion of <player X>"; the solution using a new
14367      field "player->killed" was chosen for backwards compatibility, although
14368      clever use of the fields "player->active" etc. would probably also work */
14369 #if 1
14370   if (player->killed)
14371     return;
14372 #endif
14373
14374   player->killed = TRUE;
14375
14376   /* remove accessible field at the player's position */
14377   Feld[jx][jy] = EL_EMPTY;
14378
14379   /* deactivate shield (else Bang()/Explode() would not work right) */
14380   player->shield_normal_time_left = 0;
14381   player->shield_deadly_time_left = 0;
14382
14383 #if 0
14384   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14385          player->killed, player->active, player->reanimated);
14386 #endif
14387
14388   Bang(jx, jy);
14389
14390 #if 0
14391   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14392          player->killed, player->active, player->reanimated);
14393 #endif
14394
14395 #if USE_PLAYER_REANIMATION
14396 #if 1
14397   if (player->reanimated)       /* killed player may have been reanimated */
14398     player->killed = player->reanimated = FALSE;
14399   else
14400     BuryPlayer(player);
14401 #else
14402   if (player->killed)           /* player may have been reanimated */
14403     BuryPlayer(player);
14404 #endif
14405 #else
14406   BuryPlayer(player);
14407 #endif
14408 }
14409
14410 static void KillPlayerUnlessEnemyProtected(int x, int y)
14411 {
14412   if (!PLAYER_ENEMY_PROTECTED(x, y))
14413     KillPlayer(PLAYERINFO(x, y));
14414 }
14415
14416 static void KillPlayerUnlessExplosionProtected(int x, int y)
14417 {
14418   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14419     KillPlayer(PLAYERINFO(x, y));
14420 }
14421
14422 void BuryPlayer(struct PlayerInfo *player)
14423 {
14424   int jx = player->jx, jy = player->jy;
14425
14426   if (!player->active)
14427     return;
14428
14429   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14430   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14431
14432   player->GameOver = TRUE;
14433   RemovePlayer(player);
14434 }
14435
14436 void RemovePlayer(struct PlayerInfo *player)
14437 {
14438   int jx = player->jx, jy = player->jy;
14439   int i, found = FALSE;
14440
14441   player->present = FALSE;
14442   player->active = FALSE;
14443
14444   if (!ExplodeField[jx][jy])
14445     StorePlayer[jx][jy] = 0;
14446
14447   if (player->is_moving)
14448     TEST_DrawLevelField(player->last_jx, player->last_jy);
14449
14450   for (i = 0; i < MAX_PLAYERS; i++)
14451     if (stored_player[i].active)
14452       found = TRUE;
14453
14454   if (!found)
14455     AllPlayersGone = TRUE;
14456
14457   ExitX = ZX = jx;
14458   ExitY = ZY = jy;
14459 }
14460
14461 #if USE_NEW_SNAP_DELAY
14462 static void setFieldForSnapping(int x, int y, int element, int direction)
14463 {
14464   struct ElementInfo *ei = &element_info[element];
14465   int direction_bit = MV_DIR_TO_BIT(direction);
14466   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14467   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14468                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14469
14470   Feld[x][y] = EL_ELEMENT_SNAPPING;
14471   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14472
14473   ResetGfxAnimation(x, y);
14474
14475   GfxElement[x][y] = element;
14476   GfxAction[x][y] = action;
14477   GfxDir[x][y] = direction;
14478   GfxFrame[x][y] = -1;
14479 }
14480 #endif
14481
14482 /*
14483   =============================================================================
14484   checkDiagonalPushing()
14485   -----------------------------------------------------------------------------
14486   check if diagonal input device direction results in pushing of object
14487   (by checking if the alternative direction is walkable, diggable, ...)
14488   =============================================================================
14489 */
14490
14491 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14492                                     int x, int y, int real_dx, int real_dy)
14493 {
14494   int jx, jy, dx, dy, xx, yy;
14495
14496   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14497     return TRUE;
14498
14499   /* diagonal direction: check alternative direction */
14500   jx = player->jx;
14501   jy = player->jy;
14502   dx = x - jx;
14503   dy = y - jy;
14504   xx = jx + (dx == 0 ? real_dx : 0);
14505   yy = jy + (dy == 0 ? real_dy : 0);
14506
14507   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14508 }
14509
14510 /*
14511   =============================================================================
14512   DigField()
14513   -----------------------------------------------------------------------------
14514   x, y:                 field next to player (non-diagonal) to try to dig to
14515   real_dx, real_dy:     direction as read from input device (can be diagonal)
14516   =============================================================================
14517 */
14518
14519 static int DigField(struct PlayerInfo *player,
14520                     int oldx, int oldy, int x, int y,
14521                     int real_dx, int real_dy, int mode)
14522 {
14523   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14524   boolean player_was_pushing = player->is_pushing;
14525   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14526   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14527   int jx = oldx, jy = oldy;
14528   int dx = x - jx, dy = y - jy;
14529   int nextx = x + dx, nexty = y + dy;
14530   int move_direction = (dx == -1 ? MV_LEFT  :
14531                         dx == +1 ? MV_RIGHT :
14532                         dy == -1 ? MV_UP    :
14533                         dy == +1 ? MV_DOWN  : MV_NONE);
14534   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14535   int dig_side = MV_DIR_OPPOSITE(move_direction);
14536   int old_element = Feld[jx][jy];
14537 #if USE_FIXED_DONT_RUN_INTO
14538   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14539 #else
14540   int element;
14541 #endif
14542   int collect_count;
14543
14544   if (is_player)                /* function can also be called by EL_PENGUIN */
14545   {
14546     if (player->MovPos == 0)
14547     {
14548       player->is_digging = FALSE;
14549       player->is_collecting = FALSE;
14550     }
14551
14552     if (player->MovPos == 0)    /* last pushing move finished */
14553       player->is_pushing = FALSE;
14554
14555     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14556     {
14557       player->is_switching = FALSE;
14558       player->push_delay = -1;
14559
14560       return MP_NO_ACTION;
14561     }
14562   }
14563
14564 #if !USE_FIXED_DONT_RUN_INTO
14565   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14566     return MP_NO_ACTION;
14567 #endif
14568
14569   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14570     old_element = Back[jx][jy];
14571
14572   /* in case of element dropped at player position, check background */
14573   else if (Back[jx][jy] != EL_EMPTY &&
14574            game.engine_version >= VERSION_IDENT(2,2,0,0))
14575     old_element = Back[jx][jy];
14576
14577   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14578     return MP_NO_ACTION;        /* field has no opening in this direction */
14579
14580   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14581     return MP_NO_ACTION;        /* field has no opening in this direction */
14582
14583 #if USE_FIXED_DONT_RUN_INTO
14584   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14585   {
14586     SplashAcid(x, y);
14587
14588     Feld[jx][jy] = player->artwork_element;
14589     InitMovingField(jx, jy, MV_DOWN);
14590     Store[jx][jy] = EL_ACID;
14591     ContinueMoving(jx, jy);
14592     BuryPlayer(player);
14593
14594     return MP_DONT_RUN_INTO;
14595   }
14596 #endif
14597
14598 #if USE_FIXED_DONT_RUN_INTO
14599   if (player_can_move && DONT_RUN_INTO(element))
14600   {
14601     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14602
14603     return MP_DONT_RUN_INTO;
14604   }
14605 #endif
14606
14607 #if USE_FIXED_DONT_RUN_INTO
14608   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14609     return MP_NO_ACTION;
14610 #endif
14611
14612 #if !USE_FIXED_DONT_RUN_INTO
14613   element = Feld[x][y];
14614 #endif
14615
14616   collect_count = element_info[element].collect_count_initial;
14617
14618   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14619     return MP_NO_ACTION;
14620
14621   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14622     player_can_move = player_can_move_or_snap;
14623
14624   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14625       game.engine_version >= VERSION_IDENT(2,2,0,0))
14626   {
14627     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14628                                player->index_bit, dig_side);
14629     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14630                                         player->index_bit, dig_side);
14631
14632     if (element == EL_DC_LANDMINE)
14633       Bang(x, y);
14634
14635     if (Feld[x][y] != element)          /* field changed by snapping */
14636       return MP_ACTION;
14637
14638     return MP_NO_ACTION;
14639   }
14640
14641 #if USE_PLAYER_GRAVITY
14642   if (player->gravity && is_player && !player->is_auto_moving &&
14643       canFallDown(player) && move_direction != MV_DOWN &&
14644       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14645     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14646 #else
14647   if (game.gravity && is_player && !player->is_auto_moving &&
14648       canFallDown(player) && move_direction != MV_DOWN &&
14649       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14650     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14651 #endif
14652
14653   if (player_can_move &&
14654       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14655   {
14656     int sound_element = SND_ELEMENT(element);
14657     int sound_action = ACTION_WALKING;
14658
14659     if (IS_RND_GATE(element))
14660     {
14661       if (!player->key[RND_GATE_NR(element)])
14662         return MP_NO_ACTION;
14663     }
14664     else if (IS_RND_GATE_GRAY(element))
14665     {
14666       if (!player->key[RND_GATE_GRAY_NR(element)])
14667         return MP_NO_ACTION;
14668     }
14669     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14670     {
14671       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14672         return MP_NO_ACTION;
14673     }
14674     else if (element == EL_EXIT_OPEN ||
14675              element == EL_EM_EXIT_OPEN ||
14676              element == EL_STEEL_EXIT_OPEN ||
14677              element == EL_EM_STEEL_EXIT_OPEN ||
14678              element == EL_SP_EXIT_OPEN ||
14679              element == EL_SP_EXIT_OPENING)
14680     {
14681       sound_action = ACTION_PASSING;    /* player is passing exit */
14682     }
14683     else if (element == EL_EMPTY)
14684     {
14685       sound_action = ACTION_MOVING;             /* nothing to walk on */
14686     }
14687
14688     /* play sound from background or player, whatever is available */
14689     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14690       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14691     else
14692       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14693   }
14694   else if (player_can_move &&
14695            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14696   {
14697     if (!ACCESS_FROM(element, opposite_direction))
14698       return MP_NO_ACTION;      /* field not accessible from this direction */
14699
14700     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14701       return MP_NO_ACTION;
14702
14703     if (IS_EM_GATE(element))
14704     {
14705       if (!player->key[EM_GATE_NR(element)])
14706         return MP_NO_ACTION;
14707     }
14708     else if (IS_EM_GATE_GRAY(element))
14709     {
14710       if (!player->key[EM_GATE_GRAY_NR(element)])
14711         return MP_NO_ACTION;
14712     }
14713     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14714     {
14715       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14716         return MP_NO_ACTION;
14717     }
14718     else if (IS_EMC_GATE(element))
14719     {
14720       if (!player->key[EMC_GATE_NR(element)])
14721         return MP_NO_ACTION;
14722     }
14723     else if (IS_EMC_GATE_GRAY(element))
14724     {
14725       if (!player->key[EMC_GATE_GRAY_NR(element)])
14726         return MP_NO_ACTION;
14727     }
14728     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14729     {
14730       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14731         return MP_NO_ACTION;
14732     }
14733     else if (element == EL_DC_GATE_WHITE ||
14734              element == EL_DC_GATE_WHITE_GRAY ||
14735              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14736     {
14737       if (player->num_white_keys == 0)
14738         return MP_NO_ACTION;
14739
14740       player->num_white_keys--;
14741     }
14742     else if (IS_SP_PORT(element))
14743     {
14744       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14745           element == EL_SP_GRAVITY_PORT_RIGHT ||
14746           element == EL_SP_GRAVITY_PORT_UP ||
14747           element == EL_SP_GRAVITY_PORT_DOWN)
14748 #if USE_PLAYER_GRAVITY
14749         player->gravity = !player->gravity;
14750 #else
14751         game.gravity = !game.gravity;
14752 #endif
14753       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14754                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14755                element == EL_SP_GRAVITY_ON_PORT_UP ||
14756                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14757 #if USE_PLAYER_GRAVITY
14758         player->gravity = TRUE;
14759 #else
14760         game.gravity = TRUE;
14761 #endif
14762       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14763                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14764                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14765                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14766 #if USE_PLAYER_GRAVITY
14767         player->gravity = FALSE;
14768 #else
14769         game.gravity = FALSE;
14770 #endif
14771     }
14772
14773     /* automatically move to the next field with double speed */
14774     player->programmed_action = move_direction;
14775
14776     if (player->move_delay_reset_counter == 0)
14777     {
14778       player->move_delay_reset_counter = 2;     /* two double speed steps */
14779
14780       DOUBLE_PLAYER_SPEED(player);
14781     }
14782
14783     PlayLevelSoundAction(x, y, ACTION_PASSING);
14784   }
14785   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14786   {
14787     RemoveField(x, y);
14788
14789     if (mode != DF_SNAP)
14790     {
14791       GfxElement[x][y] = GFX_ELEMENT(element);
14792       player->is_digging = TRUE;
14793     }
14794
14795     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14796
14797     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14798                                         player->index_bit, dig_side);
14799
14800     if (mode == DF_SNAP)
14801     {
14802 #if USE_NEW_SNAP_DELAY
14803       if (level.block_snap_field)
14804         setFieldForSnapping(x, y, element, move_direction);
14805       else
14806         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14807 #else
14808       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14809 #endif
14810
14811       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14812                                           player->index_bit, dig_side);
14813     }
14814   }
14815   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14816   {
14817     RemoveField(x, y);
14818
14819     if (is_player && mode != DF_SNAP)
14820     {
14821       GfxElement[x][y] = element;
14822       player->is_collecting = TRUE;
14823     }
14824
14825     if (element == EL_SPEED_PILL)
14826     {
14827       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14828     }
14829     else if (element == EL_EXTRA_TIME && level.time > 0)
14830     {
14831       TimeLeft += level.extra_time;
14832
14833 #if 1
14834       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14835
14836       DisplayGameControlValues();
14837 #else
14838       DrawGameValue_Time(TimeLeft);
14839 #endif
14840     }
14841     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14842     {
14843       player->shield_normal_time_left += level.shield_normal_time;
14844       if (element == EL_SHIELD_DEADLY)
14845         player->shield_deadly_time_left += level.shield_deadly_time;
14846     }
14847     else if (element == EL_DYNAMITE ||
14848              element == EL_EM_DYNAMITE ||
14849              element == EL_SP_DISK_RED)
14850     {
14851       if (player->inventory_size < MAX_INVENTORY_SIZE)
14852         player->inventory_element[player->inventory_size++] = element;
14853
14854       DrawGameDoorValues();
14855     }
14856     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14857     {
14858       player->dynabomb_count++;
14859       player->dynabombs_left++;
14860     }
14861     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14862     {
14863       player->dynabomb_size++;
14864     }
14865     else if (element == EL_DYNABOMB_INCREASE_POWER)
14866     {
14867       player->dynabomb_xl = TRUE;
14868     }
14869     else if (IS_KEY(element))
14870     {
14871       player->key[KEY_NR(element)] = TRUE;
14872
14873       DrawGameDoorValues();
14874     }
14875     else if (element == EL_DC_KEY_WHITE)
14876     {
14877       player->num_white_keys++;
14878
14879       /* display white keys? */
14880       /* DrawGameDoorValues(); */
14881     }
14882     else if (IS_ENVELOPE(element))
14883     {
14884       player->show_envelope = element;
14885     }
14886     else if (element == EL_EMC_LENSES)
14887     {
14888       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14889
14890       RedrawAllInvisibleElementsForLenses();
14891     }
14892     else if (element == EL_EMC_MAGNIFIER)
14893     {
14894       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14895
14896       RedrawAllInvisibleElementsForMagnifier();
14897     }
14898     else if (IS_DROPPABLE(element) ||
14899              IS_THROWABLE(element))     /* can be collected and dropped */
14900     {
14901       int i;
14902
14903       if (collect_count == 0)
14904         player->inventory_infinite_element = element;
14905       else
14906         for (i = 0; i < collect_count; i++)
14907           if (player->inventory_size < MAX_INVENTORY_SIZE)
14908             player->inventory_element[player->inventory_size++] = element;
14909
14910       DrawGameDoorValues();
14911     }
14912     else if (collect_count > 0)
14913     {
14914       local_player->gems_still_needed -= collect_count;
14915       if (local_player->gems_still_needed < 0)
14916         local_player->gems_still_needed = 0;
14917
14918 #if 1
14919       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14920
14921       DisplayGameControlValues();
14922 #else
14923       DrawGameValue_Emeralds(local_player->gems_still_needed);
14924 #endif
14925     }
14926
14927     RaiseScoreElement(element);
14928     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14929
14930     if (is_player)
14931       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14932                                           player->index_bit, dig_side);
14933
14934     if (mode == DF_SNAP)
14935     {
14936 #if USE_NEW_SNAP_DELAY
14937       if (level.block_snap_field)
14938         setFieldForSnapping(x, y, element, move_direction);
14939       else
14940         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14941 #else
14942       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14943 #endif
14944
14945       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14946                                           player->index_bit, dig_side);
14947     }
14948   }
14949   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14950   {
14951     if (mode == DF_SNAP && element != EL_BD_ROCK)
14952       return MP_NO_ACTION;
14953
14954     if (CAN_FALL(element) && dy)
14955       return MP_NO_ACTION;
14956
14957     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14958         !(element == EL_SPRING && level.use_spring_bug))
14959       return MP_NO_ACTION;
14960
14961     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14962         ((move_direction & MV_VERTICAL &&
14963           ((element_info[element].move_pattern & MV_LEFT &&
14964             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14965            (element_info[element].move_pattern & MV_RIGHT &&
14966             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14967          (move_direction & MV_HORIZONTAL &&
14968           ((element_info[element].move_pattern & MV_UP &&
14969             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14970            (element_info[element].move_pattern & MV_DOWN &&
14971             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14972       return MP_NO_ACTION;
14973
14974     /* do not push elements already moving away faster than player */
14975     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14976         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14977       return MP_NO_ACTION;
14978
14979     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14980     {
14981       if (player->push_delay_value == -1 || !player_was_pushing)
14982         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14983     }
14984     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14985     {
14986       if (player->push_delay_value == -1)
14987         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14988     }
14989     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14990     {
14991       if (!player->is_pushing)
14992         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14993     }
14994
14995     player->is_pushing = TRUE;
14996     player->is_active = TRUE;
14997
14998     if (!(IN_LEV_FIELD(nextx, nexty) &&
14999           (IS_FREE(nextx, nexty) ||
15000            (IS_SB_ELEMENT(element) &&
15001             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15002            (IS_CUSTOM_ELEMENT(element) &&
15003             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15004       return MP_NO_ACTION;
15005
15006     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15007       return MP_NO_ACTION;
15008
15009     if (player->push_delay == -1)       /* new pushing; restart delay */
15010       player->push_delay = 0;
15011
15012     if (player->push_delay < player->push_delay_value &&
15013         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15014         element != EL_SPRING && element != EL_BALLOON)
15015     {
15016       /* make sure that there is no move delay before next try to push */
15017       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15018         player->move_delay = 0;
15019
15020       return MP_NO_ACTION;
15021     }
15022
15023     if (IS_CUSTOM_ELEMENT(element) &&
15024         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15025     {
15026       if (!DigFieldByCE(nextx, nexty, element))
15027         return MP_NO_ACTION;
15028     }
15029
15030     if (IS_SB_ELEMENT(element))
15031     {
15032       if (element == EL_SOKOBAN_FIELD_FULL)
15033       {
15034         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15035         local_player->sokobanfields_still_needed++;
15036       }
15037
15038       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15039       {
15040         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15041         local_player->sokobanfields_still_needed--;
15042       }
15043
15044       Feld[x][y] = EL_SOKOBAN_OBJECT;
15045
15046       if (Back[x][y] == Back[nextx][nexty])
15047         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15048       else if (Back[x][y] != 0)
15049         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15050                                     ACTION_EMPTYING);
15051       else
15052         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15053                                     ACTION_FILLING);
15054
15055       if (local_player->sokobanfields_still_needed == 0 &&
15056           game.emulation == EMU_SOKOBAN)
15057       {
15058         PlayerWins(player);
15059
15060         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15061       }
15062     }
15063     else
15064       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15065
15066     InitMovingField(x, y, move_direction);
15067     GfxAction[x][y] = ACTION_PUSHING;
15068
15069     if (mode == DF_SNAP)
15070       ContinueMoving(x, y);
15071     else
15072       MovPos[x][y] = (dx != 0 ? dx : dy);
15073
15074     Pushed[x][y] = TRUE;
15075     Pushed[nextx][nexty] = TRUE;
15076
15077     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15078       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15079     else
15080       player->push_delay_value = -1;    /* get new value later */
15081
15082     /* check for element change _after_ element has been pushed */
15083     if (game.use_change_when_pushing_bug)
15084     {
15085       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15086                                  player->index_bit, dig_side);
15087       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15088                                           player->index_bit, dig_side);
15089     }
15090   }
15091   else if (IS_SWITCHABLE(element))
15092   {
15093     if (PLAYER_SWITCHING(player, x, y))
15094     {
15095       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15096                                           player->index_bit, dig_side);
15097
15098       return MP_ACTION;
15099     }
15100
15101     player->is_switching = TRUE;
15102     player->switch_x = x;
15103     player->switch_y = y;
15104
15105     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15106
15107     if (element == EL_ROBOT_WHEEL)
15108     {
15109       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15110       ZX = x;
15111       ZY = y;
15112
15113       game.robot_wheel_active = TRUE;
15114
15115       TEST_DrawLevelField(x, y);
15116     }
15117     else if (element == EL_SP_TERMINAL)
15118     {
15119       int xx, yy;
15120
15121       SCAN_PLAYFIELD(xx, yy)
15122       {
15123         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15124           Bang(xx, yy);
15125         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15126           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15127       }
15128     }
15129     else if (IS_BELT_SWITCH(element))
15130     {
15131       ToggleBeltSwitch(x, y);
15132     }
15133     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15134              element == EL_SWITCHGATE_SWITCH_DOWN ||
15135              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15136              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15137     {
15138       ToggleSwitchgateSwitch(x, y);
15139     }
15140     else if (element == EL_LIGHT_SWITCH ||
15141              element == EL_LIGHT_SWITCH_ACTIVE)
15142     {
15143       ToggleLightSwitch(x, y);
15144     }
15145     else if (element == EL_TIMEGATE_SWITCH ||
15146              element == EL_DC_TIMEGATE_SWITCH)
15147     {
15148       ActivateTimegateSwitch(x, y);
15149     }
15150     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15151              element == EL_BALLOON_SWITCH_RIGHT ||
15152              element == EL_BALLOON_SWITCH_UP    ||
15153              element == EL_BALLOON_SWITCH_DOWN  ||
15154              element == EL_BALLOON_SWITCH_NONE  ||
15155              element == EL_BALLOON_SWITCH_ANY)
15156     {
15157       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15158                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15159                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15160                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15161                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15162                              move_direction);
15163     }
15164     else if (element == EL_LAMP)
15165     {
15166       Feld[x][y] = EL_LAMP_ACTIVE;
15167       local_player->lights_still_needed--;
15168
15169       ResetGfxAnimation(x, y);
15170       TEST_DrawLevelField(x, y);
15171     }
15172     else if (element == EL_TIME_ORB_FULL)
15173     {
15174       Feld[x][y] = EL_TIME_ORB_EMPTY;
15175
15176       if (level.time > 0 || level.use_time_orb_bug)
15177       {
15178         TimeLeft += level.time_orb_time;
15179
15180 #if 1
15181         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15182
15183         DisplayGameControlValues();
15184 #else
15185         DrawGameValue_Time(TimeLeft);
15186 #endif
15187       }
15188
15189       ResetGfxAnimation(x, y);
15190       TEST_DrawLevelField(x, y);
15191     }
15192     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15193              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15194     {
15195       int xx, yy;
15196
15197       game.ball_state = !game.ball_state;
15198
15199       SCAN_PLAYFIELD(xx, yy)
15200       {
15201         int e = Feld[xx][yy];
15202
15203         if (game.ball_state)
15204         {
15205           if (e == EL_EMC_MAGIC_BALL)
15206             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15207           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15208             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15209         }
15210         else
15211         {
15212           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15213             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15214           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15215             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15216         }
15217       }
15218     }
15219
15220     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15221                                         player->index_bit, dig_side);
15222
15223     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15224                                         player->index_bit, dig_side);
15225
15226     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15227                                         player->index_bit, dig_side);
15228
15229     return MP_ACTION;
15230   }
15231   else
15232   {
15233     if (!PLAYER_SWITCHING(player, x, y))
15234     {
15235       player->is_switching = TRUE;
15236       player->switch_x = x;
15237       player->switch_y = y;
15238
15239       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15240                                  player->index_bit, dig_side);
15241       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15242                                           player->index_bit, dig_side);
15243
15244       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15245                                  player->index_bit, dig_side);
15246       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15247                                           player->index_bit, dig_side);
15248     }
15249
15250     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15251                                player->index_bit, dig_side);
15252     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15253                                         player->index_bit, dig_side);
15254
15255     return MP_NO_ACTION;
15256   }
15257
15258   player->push_delay = -1;
15259
15260   if (is_player)                /* function can also be called by EL_PENGUIN */
15261   {
15262     if (Feld[x][y] != element)          /* really digged/collected something */
15263     {
15264       player->is_collecting = !player->is_digging;
15265       player->is_active = TRUE;
15266     }
15267   }
15268
15269   return MP_MOVING;
15270 }
15271
15272 static boolean DigFieldByCE(int x, int y, int digging_element)
15273 {
15274   int element = Feld[x][y];
15275
15276   if (!IS_FREE(x, y))
15277   {
15278     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15279                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15280                   ACTION_BREAKING);
15281
15282     /* no element can dig solid indestructible elements */
15283     if (IS_INDESTRUCTIBLE(element) &&
15284         !IS_DIGGABLE(element) &&
15285         !IS_COLLECTIBLE(element))
15286       return FALSE;
15287
15288     if (AmoebaNr[x][y] &&
15289         (element == EL_AMOEBA_FULL ||
15290          element == EL_BD_AMOEBA ||
15291          element == EL_AMOEBA_GROWING))
15292     {
15293       AmoebaCnt[AmoebaNr[x][y]]--;
15294       AmoebaCnt2[AmoebaNr[x][y]]--;
15295     }
15296
15297     if (IS_MOVING(x, y))
15298       RemoveMovingField(x, y);
15299     else
15300     {
15301       RemoveField(x, y);
15302       TEST_DrawLevelField(x, y);
15303     }
15304
15305     /* if digged element was about to explode, prevent the explosion */
15306     ExplodeField[x][y] = EX_TYPE_NONE;
15307
15308     PlayLevelSoundAction(x, y, action);
15309   }
15310
15311   Store[x][y] = EL_EMPTY;
15312
15313 #if 1
15314   /* this makes it possible to leave the removed element again */
15315   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15316     Store[x][y] = element;
15317 #else
15318   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15319   {
15320     int move_leave_element = element_info[digging_element].move_leave_element;
15321
15322     /* this makes it possible to leave the removed element again */
15323     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15324                    element : move_leave_element);
15325   }
15326 #endif
15327
15328   return TRUE;
15329 }
15330
15331 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15332 {
15333   int jx = player->jx, jy = player->jy;
15334   int x = jx + dx, y = jy + dy;
15335   int snap_direction = (dx == -1 ? MV_LEFT  :
15336                         dx == +1 ? MV_RIGHT :
15337                         dy == -1 ? MV_UP    :
15338                         dy == +1 ? MV_DOWN  : MV_NONE);
15339   boolean can_continue_snapping = (level.continuous_snapping &&
15340                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15341
15342   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15343     return FALSE;
15344
15345   if (!player->active || !IN_LEV_FIELD(x, y))
15346     return FALSE;
15347
15348   if (dx && dy)
15349     return FALSE;
15350
15351   if (!dx && !dy)
15352   {
15353     if (player->MovPos == 0)
15354       player->is_pushing = FALSE;
15355
15356     player->is_snapping = FALSE;
15357
15358     if (player->MovPos == 0)
15359     {
15360       player->is_moving = FALSE;
15361       player->is_digging = FALSE;
15362       player->is_collecting = FALSE;
15363     }
15364
15365     return FALSE;
15366   }
15367
15368 #if USE_NEW_CONTINUOUS_SNAPPING
15369   /* prevent snapping with already pressed snap key when not allowed */
15370   if (player->is_snapping && !can_continue_snapping)
15371     return FALSE;
15372 #else
15373   if (player->is_snapping)
15374     return FALSE;
15375 #endif
15376
15377   player->MovDir = snap_direction;
15378
15379   if (player->MovPos == 0)
15380   {
15381     player->is_moving = FALSE;
15382     player->is_digging = FALSE;
15383     player->is_collecting = FALSE;
15384   }
15385
15386   player->is_dropping = FALSE;
15387   player->is_dropping_pressed = FALSE;
15388   player->drop_pressed_delay = 0;
15389
15390   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15391     return FALSE;
15392
15393   player->is_snapping = TRUE;
15394   player->is_active = TRUE;
15395
15396   if (player->MovPos == 0)
15397   {
15398     player->is_moving = FALSE;
15399     player->is_digging = FALSE;
15400     player->is_collecting = FALSE;
15401   }
15402
15403   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15404     TEST_DrawLevelField(player->last_jx, player->last_jy);
15405
15406   TEST_DrawLevelField(x, y);
15407
15408   return TRUE;
15409 }
15410
15411 static boolean DropElement(struct PlayerInfo *player)
15412 {
15413   int old_element, new_element;
15414   int dropx = player->jx, dropy = player->jy;
15415   int drop_direction = player->MovDir;
15416   int drop_side = drop_direction;
15417 #if 1
15418   int drop_element = get_next_dropped_element(player);
15419 #else
15420   int drop_element = (player->inventory_size > 0 ?
15421                       player->inventory_element[player->inventory_size - 1] :
15422                       player->inventory_infinite_element != EL_UNDEFINED ?
15423                       player->inventory_infinite_element :
15424                       player->dynabombs_left > 0 ?
15425                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15426                       EL_UNDEFINED);
15427 #endif
15428
15429   player->is_dropping_pressed = TRUE;
15430
15431   /* do not drop an element on top of another element; when holding drop key
15432      pressed without moving, dropped element must move away before the next
15433      element can be dropped (this is especially important if the next element
15434      is dynamite, which can be placed on background for historical reasons) */
15435   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15436     return MP_ACTION;
15437
15438   if (IS_THROWABLE(drop_element))
15439   {
15440     dropx += GET_DX_FROM_DIR(drop_direction);
15441     dropy += GET_DY_FROM_DIR(drop_direction);
15442
15443     if (!IN_LEV_FIELD(dropx, dropy))
15444       return FALSE;
15445   }
15446
15447   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15448   new_element = drop_element;           /* default: no change when dropping */
15449
15450   /* check if player is active, not moving and ready to drop */
15451   if (!player->active || player->MovPos || player->drop_delay > 0)
15452     return FALSE;
15453
15454   /* check if player has anything that can be dropped */
15455   if (new_element == EL_UNDEFINED)
15456     return FALSE;
15457
15458   /* check if drop key was pressed long enough for EM style dynamite */
15459   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15460     return FALSE;
15461
15462   /* check if anything can be dropped at the current position */
15463   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15464     return FALSE;
15465
15466   /* collected custom elements can only be dropped on empty fields */
15467   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15468     return FALSE;
15469
15470   if (old_element != EL_EMPTY)
15471     Back[dropx][dropy] = old_element;   /* store old element on this field */
15472
15473   ResetGfxAnimation(dropx, dropy);
15474   ResetRandomAnimationValue(dropx, dropy);
15475
15476   if (player->inventory_size > 0 ||
15477       player->inventory_infinite_element != EL_UNDEFINED)
15478   {
15479     if (player->inventory_size > 0)
15480     {
15481       player->inventory_size--;
15482
15483       DrawGameDoorValues();
15484
15485       if (new_element == EL_DYNAMITE)
15486         new_element = EL_DYNAMITE_ACTIVE;
15487       else if (new_element == EL_EM_DYNAMITE)
15488         new_element = EL_EM_DYNAMITE_ACTIVE;
15489       else if (new_element == EL_SP_DISK_RED)
15490         new_element = EL_SP_DISK_RED_ACTIVE;
15491     }
15492
15493     Feld[dropx][dropy] = new_element;
15494
15495     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15496       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15497                           el2img(Feld[dropx][dropy]), 0);
15498
15499     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15500
15501     /* needed if previous element just changed to "empty" in the last frame */
15502     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15503
15504     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15505                                player->index_bit, drop_side);
15506     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15507                                         CE_PLAYER_DROPS_X,
15508                                         player->index_bit, drop_side);
15509
15510     TestIfElementTouchesCustomElement(dropx, dropy);
15511   }
15512   else          /* player is dropping a dyna bomb */
15513   {
15514     player->dynabombs_left--;
15515
15516     Feld[dropx][dropy] = new_element;
15517
15518     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15519       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15520                           el2img(Feld[dropx][dropy]), 0);
15521
15522     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15523   }
15524
15525   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15526     InitField_WithBug1(dropx, dropy, FALSE);
15527
15528   new_element = Feld[dropx][dropy];     /* element might have changed */
15529
15530   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15531       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15532   {
15533     int move_direction, nextx, nexty;
15534
15535     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15536       MovDir[dropx][dropy] = drop_direction;
15537
15538     move_direction = MovDir[dropx][dropy];
15539     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15540     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15541
15542     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15543
15544 #if USE_FIX_IMPACT_COLLISION
15545     /* do not cause impact style collision by dropping elements that can fall */
15546     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15547 #else
15548     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15549 #endif
15550   }
15551
15552   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15553   player->is_dropping = TRUE;
15554
15555   player->drop_pressed_delay = 0;
15556   player->is_dropping_pressed = FALSE;
15557
15558   player->drop_x = dropx;
15559   player->drop_y = dropy;
15560
15561   return TRUE;
15562 }
15563
15564 /* ------------------------------------------------------------------------- */
15565 /* game sound playing functions                                              */
15566 /* ------------------------------------------------------------------------- */
15567
15568 static int *loop_sound_frame = NULL;
15569 static int *loop_sound_volume = NULL;
15570
15571 void InitPlayLevelSound()
15572 {
15573   int num_sounds = getSoundListSize();
15574
15575   checked_free(loop_sound_frame);
15576   checked_free(loop_sound_volume);
15577
15578   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15579   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15580 }
15581
15582 static void PlayLevelSound(int x, int y, int nr)
15583 {
15584   int sx = SCREENX(x), sy = SCREENY(y);
15585   int volume, stereo_position;
15586   int max_distance = 8;
15587   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15588
15589   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15590       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15591     return;
15592
15593   if (!IN_LEV_FIELD(x, y) ||
15594       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15595       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15596     return;
15597
15598   volume = SOUND_MAX_VOLUME;
15599
15600   if (!IN_SCR_FIELD(sx, sy))
15601   {
15602     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15603     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15604
15605     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15606   }
15607
15608   stereo_position = (SOUND_MAX_LEFT +
15609                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15610                      (SCR_FIELDX + 2 * max_distance));
15611
15612   if (IS_LOOP_SOUND(nr))
15613   {
15614     /* This assures that quieter loop sounds do not overwrite louder ones,
15615        while restarting sound volume comparison with each new game frame. */
15616
15617     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15618       return;
15619
15620     loop_sound_volume[nr] = volume;
15621     loop_sound_frame[nr] = FrameCounter;
15622   }
15623
15624   PlaySoundExt(nr, volume, stereo_position, type);
15625 }
15626
15627 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15628 {
15629   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15630                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15631                  y < LEVELY(BY1) ? LEVELY(BY1) :
15632                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15633                  sound_action);
15634 }
15635
15636 static void PlayLevelSoundAction(int x, int y, int action)
15637 {
15638   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15639 }
15640
15641 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15642 {
15643   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15644
15645   if (sound_effect != SND_UNDEFINED)
15646     PlayLevelSound(x, y, sound_effect);
15647 }
15648
15649 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15650                                               int action)
15651 {
15652   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15653
15654   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15655     PlayLevelSound(x, y, sound_effect);
15656 }
15657
15658 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15659 {
15660   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15661
15662   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15663     PlayLevelSound(x, y, sound_effect);
15664 }
15665
15666 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15667 {
15668   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15669
15670   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15671     StopSound(sound_effect);
15672 }
15673
15674 static void PlayLevelMusic()
15675 {
15676   if (levelset.music[level_nr] != MUS_UNDEFINED)
15677     PlayMusic(levelset.music[level_nr]);        /* from config file */
15678   else
15679     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15680 }
15681
15682 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15683 {
15684   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15685   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15686   int x = xx - 1 - offset;
15687   int y = yy - 1 - offset;
15688
15689   switch (sample)
15690   {
15691     case SAMPLE_blank:
15692       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15693       break;
15694
15695     case SAMPLE_roll:
15696       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15697       break;
15698
15699     case SAMPLE_stone:
15700       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15701       break;
15702
15703     case SAMPLE_nut:
15704       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15705       break;
15706
15707     case SAMPLE_crack:
15708       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15709       break;
15710
15711     case SAMPLE_bug:
15712       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15713       break;
15714
15715     case SAMPLE_tank:
15716       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15717       break;
15718
15719     case SAMPLE_android_clone:
15720       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15721       break;
15722
15723     case SAMPLE_android_move:
15724       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15725       break;
15726
15727     case SAMPLE_spring:
15728       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15729       break;
15730
15731     case SAMPLE_slurp:
15732       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15733       break;
15734
15735     case SAMPLE_eater:
15736       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15737       break;
15738
15739     case SAMPLE_eater_eat:
15740       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15741       break;
15742
15743     case SAMPLE_alien:
15744       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15745       break;
15746
15747     case SAMPLE_collect:
15748       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15749       break;
15750
15751     case SAMPLE_diamond:
15752       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15753       break;
15754
15755     case SAMPLE_squash:
15756       /* !!! CHECK THIS !!! */
15757 #if 1
15758       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15759 #else
15760       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15761 #endif
15762       break;
15763
15764     case SAMPLE_wonderfall:
15765       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15766       break;
15767
15768     case SAMPLE_drip:
15769       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15770       break;
15771
15772     case SAMPLE_push:
15773       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15774       break;
15775
15776     case SAMPLE_dirt:
15777       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15778       break;
15779
15780     case SAMPLE_acid:
15781       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15782       break;
15783
15784     case SAMPLE_ball:
15785       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15786       break;
15787
15788     case SAMPLE_grow:
15789       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15790       break;
15791
15792     case SAMPLE_wonder:
15793       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15794       break;
15795
15796     case SAMPLE_door:
15797       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15798       break;
15799
15800     case SAMPLE_exit_open:
15801       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15802       break;
15803
15804     case SAMPLE_exit_leave:
15805       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15806       break;
15807
15808     case SAMPLE_dynamite:
15809       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15810       break;
15811
15812     case SAMPLE_tick:
15813       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15814       break;
15815
15816     case SAMPLE_press:
15817       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15818       break;
15819
15820     case SAMPLE_wheel:
15821       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15822       break;
15823
15824     case SAMPLE_boom:
15825       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15826       break;
15827
15828     case SAMPLE_die:
15829       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15830       break;
15831
15832     case SAMPLE_time:
15833       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15834       break;
15835
15836     default:
15837       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15838       break;
15839   }
15840 }
15841
15842 #if 0
15843 void ChangeTime(int value)
15844 {
15845   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15846
15847   *time += value;
15848
15849   /* EMC game engine uses value from time counter of RND game engine */
15850   level.native_em_level->lev->time = *time;
15851
15852   DrawGameValue_Time(*time);
15853 }
15854
15855 void RaiseScore(int value)
15856 {
15857   /* EMC game engine and RND game engine have separate score counters */
15858   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15859                 &level.native_em_level->lev->score : &local_player->score);
15860
15861   *score += value;
15862
15863   DrawGameValue_Score(*score);
15864 }
15865 #endif
15866
15867 void RaiseScore(int value)
15868 {
15869   local_player->score += value;
15870
15871 #if 1
15872   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15873
15874   DisplayGameControlValues();
15875 #else
15876   DrawGameValue_Score(local_player->score);
15877 #endif
15878 }
15879
15880 void RaiseScoreElement(int element)
15881 {
15882   switch (element)
15883   {
15884     case EL_EMERALD:
15885     case EL_BD_DIAMOND:
15886     case EL_EMERALD_YELLOW:
15887     case EL_EMERALD_RED:
15888     case EL_EMERALD_PURPLE:
15889     case EL_SP_INFOTRON:
15890       RaiseScore(level.score[SC_EMERALD]);
15891       break;
15892     case EL_DIAMOND:
15893       RaiseScore(level.score[SC_DIAMOND]);
15894       break;
15895     case EL_CRYSTAL:
15896       RaiseScore(level.score[SC_CRYSTAL]);
15897       break;
15898     case EL_PEARL:
15899       RaiseScore(level.score[SC_PEARL]);
15900       break;
15901     case EL_BUG:
15902     case EL_BD_BUTTERFLY:
15903     case EL_SP_ELECTRON:
15904       RaiseScore(level.score[SC_BUG]);
15905       break;
15906     case EL_SPACESHIP:
15907     case EL_BD_FIREFLY:
15908     case EL_SP_SNIKSNAK:
15909       RaiseScore(level.score[SC_SPACESHIP]);
15910       break;
15911     case EL_YAMYAM:
15912     case EL_DARK_YAMYAM:
15913       RaiseScore(level.score[SC_YAMYAM]);
15914       break;
15915     case EL_ROBOT:
15916       RaiseScore(level.score[SC_ROBOT]);
15917       break;
15918     case EL_PACMAN:
15919       RaiseScore(level.score[SC_PACMAN]);
15920       break;
15921     case EL_NUT:
15922       RaiseScore(level.score[SC_NUT]);
15923       break;
15924     case EL_DYNAMITE:
15925     case EL_EM_DYNAMITE:
15926     case EL_SP_DISK_RED:
15927     case EL_DYNABOMB_INCREASE_NUMBER:
15928     case EL_DYNABOMB_INCREASE_SIZE:
15929     case EL_DYNABOMB_INCREASE_POWER:
15930       RaiseScore(level.score[SC_DYNAMITE]);
15931       break;
15932     case EL_SHIELD_NORMAL:
15933     case EL_SHIELD_DEADLY:
15934       RaiseScore(level.score[SC_SHIELD]);
15935       break;
15936     case EL_EXTRA_TIME:
15937       RaiseScore(level.extra_time_score);
15938       break;
15939     case EL_KEY_1:
15940     case EL_KEY_2:
15941     case EL_KEY_3:
15942     case EL_KEY_4:
15943     case EL_EM_KEY_1:
15944     case EL_EM_KEY_2:
15945     case EL_EM_KEY_3:
15946     case EL_EM_KEY_4:
15947     case EL_EMC_KEY_5:
15948     case EL_EMC_KEY_6:
15949     case EL_EMC_KEY_7:
15950     case EL_EMC_KEY_8:
15951     case EL_DC_KEY_WHITE:
15952       RaiseScore(level.score[SC_KEY]);
15953       break;
15954     default:
15955       RaiseScore(element_info[element].collect_score);
15956       break;
15957   }
15958 }
15959
15960 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15961 {
15962   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15963   {
15964 #if defined(NETWORK_AVALIABLE)
15965     if (options.network)
15966       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15967     else
15968 #endif
15969     {
15970       if (quick_quit)
15971       {
15972 #if 1
15973
15974 #if 1
15975         FadeSkipNextFadeIn();
15976 #else
15977         fading = fading_none;
15978 #endif
15979
15980 #else
15981         OpenDoor(DOOR_CLOSE_1);
15982 #endif
15983
15984         game_status = GAME_MODE_MAIN;
15985
15986 #if 1
15987         DrawAndFadeInMainMenu(REDRAW_FIELD);
15988 #else
15989         DrawMainMenu();
15990 #endif
15991       }
15992       else
15993       {
15994 #if 0
15995         FadeOut(REDRAW_FIELD);
15996 #endif
15997
15998         game_status = GAME_MODE_MAIN;
15999
16000         DrawAndFadeInMainMenu(REDRAW_FIELD);
16001       }
16002     }
16003   }
16004   else          /* continue playing the game */
16005   {
16006     if (tape.playing && tape.deactivate_display)
16007       TapeDeactivateDisplayOff(TRUE);
16008
16009     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16010
16011     if (tape.playing && tape.deactivate_display)
16012       TapeDeactivateDisplayOn();
16013   }
16014 }
16015
16016 void RequestQuitGame(boolean ask_if_really_quit)
16017 {
16018   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16019   boolean skip_request = AllPlayersGone || quick_quit;
16020
16021   RequestQuitGameExt(skip_request, quick_quit,
16022                      "Do you really want to quit the game ?");
16023 }
16024
16025
16026 /* ------------------------------------------------------------------------- */
16027 /* random generator functions                                                */
16028 /* ------------------------------------------------------------------------- */
16029
16030 unsigned int InitEngineRandom_RND(long seed)
16031 {
16032   game.num_random_calls = 0;
16033
16034 #if 0
16035   unsigned int rnd_seed = InitEngineRandom(seed);
16036
16037   printf("::: START RND: %d\n", rnd_seed);
16038
16039   return rnd_seed;
16040 #else
16041
16042   return InitEngineRandom(seed);
16043
16044 #endif
16045
16046 }
16047
16048 unsigned int RND(int max)
16049 {
16050   if (max > 0)
16051   {
16052     game.num_random_calls++;
16053
16054     return GetEngineRandom(max);
16055   }
16056
16057   return 0;
16058 }
16059
16060
16061 /* ------------------------------------------------------------------------- */
16062 /* game engine snapshot handling functions                                   */
16063 /* ------------------------------------------------------------------------- */
16064
16065 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16066
16067 struct EngineSnapshotInfo
16068 {
16069   /* runtime values for custom element collect score */
16070   int collect_score[NUM_CUSTOM_ELEMENTS];
16071
16072   /* runtime values for group element choice position */
16073   int choice_pos[NUM_GROUP_ELEMENTS];
16074
16075   /* runtime values for belt position animations */
16076   int belt_graphic[4 * NUM_BELT_PARTS];
16077   int belt_anim_mode[4 * NUM_BELT_PARTS];
16078 };
16079
16080 struct EngineSnapshotNodeInfo
16081 {
16082   void *buffer_orig;
16083   void *buffer_copy;
16084   int size;
16085 };
16086
16087 static struct EngineSnapshotInfo engine_snapshot_rnd;
16088 static ListNode *engine_snapshot_list = NULL;
16089 static char *snapshot_level_identifier = NULL;
16090 static int snapshot_level_nr = -1;
16091
16092 void FreeEngineSnapshot()
16093 {
16094   while (engine_snapshot_list != NULL)
16095     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16096                        checked_free);
16097
16098   setString(&snapshot_level_identifier, NULL);
16099   snapshot_level_nr = -1;
16100 }
16101
16102 static void SaveEngineSnapshotValues_RND()
16103 {
16104   static int belt_base_active_element[4] =
16105   {
16106     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16107     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16108     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16109     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16110   };
16111   int i, j;
16112
16113   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16114   {
16115     int element = EL_CUSTOM_START + i;
16116
16117     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16118   }
16119
16120   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16121   {
16122     int element = EL_GROUP_START + i;
16123
16124     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16125   }
16126
16127   for (i = 0; i < 4; i++)
16128   {
16129     for (j = 0; j < NUM_BELT_PARTS; j++)
16130     {
16131       int element = belt_base_active_element[i] + j;
16132       int graphic = el2img(element);
16133       int anim_mode = graphic_info[graphic].anim_mode;
16134
16135       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16136       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16137     }
16138   }
16139 }
16140
16141 static void LoadEngineSnapshotValues_RND()
16142 {
16143   unsigned long num_random_calls = game.num_random_calls;
16144   int i, j;
16145
16146   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16147   {
16148     int element = EL_CUSTOM_START + i;
16149
16150     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16151   }
16152
16153   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16154   {
16155     int element = EL_GROUP_START + i;
16156
16157     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16158   }
16159
16160   for (i = 0; i < 4; i++)
16161   {
16162     for (j = 0; j < NUM_BELT_PARTS; j++)
16163     {
16164       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16165       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16166
16167       graphic_info[graphic].anim_mode = anim_mode;
16168     }
16169   }
16170
16171   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16172   {
16173     InitRND(tape.random_seed);
16174     for (i = 0; i < num_random_calls; i++)
16175       RND(1);
16176   }
16177
16178   if (game.num_random_calls != num_random_calls)
16179   {
16180     Error(ERR_INFO, "number of random calls out of sync");
16181     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16182     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16183     Error(ERR_EXIT, "this should not happen -- please debug");
16184   }
16185 }
16186
16187 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16188 {
16189   struct EngineSnapshotNodeInfo *bi =
16190     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16191
16192   bi->buffer_orig = buffer;
16193   bi->buffer_copy = checked_malloc(size);
16194   bi->size = size;
16195
16196   memcpy(bi->buffer_copy, buffer, size);
16197
16198   addNodeToList(&engine_snapshot_list, NULL, bi);
16199 }
16200
16201 void SaveEngineSnapshot()
16202 {
16203   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16204
16205   if (level_editor_test_game)   /* do not save snapshots from editor */
16206     return;
16207
16208   /* copy some special values to a structure better suited for the snapshot */
16209
16210   SaveEngineSnapshotValues_RND();
16211   SaveEngineSnapshotValues_EM();
16212
16213   /* save values stored in special snapshot structure */
16214
16215   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16216   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16217
16218   /* save further RND engine values */
16219
16220   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16221   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16222   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16223
16224   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16225   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16226   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16227   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16228
16229   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16230   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16231   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16232   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16233   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16234
16235   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16236   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16237   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16238
16239   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16240
16241   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16242
16243   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16244   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16245
16246   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16247   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16248   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16249   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16250   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16251   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16252   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16253   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16254   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16255   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16256   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16257   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16258   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16259   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16260   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16261   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16262   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16263   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16264
16265   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16266   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16267
16268   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16269   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16270   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16271
16272   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16273   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16274
16275   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16276   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16277   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16278   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16279   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16280
16281   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16282   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16283
16284   /* save level identification information */
16285
16286   setString(&snapshot_level_identifier, leveldir_current->identifier);
16287   snapshot_level_nr = level_nr;
16288
16289 #if 0
16290   ListNode *node = engine_snapshot_list;
16291   int num_bytes = 0;
16292
16293   while (node != NULL)
16294   {
16295     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16296
16297     node = node->next;
16298   }
16299
16300   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16301 #endif
16302 }
16303
16304 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16305 {
16306   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16307 }
16308
16309 void LoadEngineSnapshot()
16310 {
16311   ListNode *node = engine_snapshot_list;
16312
16313   if (engine_snapshot_list == NULL)
16314     return;
16315
16316   while (node != NULL)
16317   {
16318     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16319
16320     node = node->next;
16321   }
16322
16323   /* restore special values from snapshot structure */
16324
16325   LoadEngineSnapshotValues_RND();
16326   LoadEngineSnapshotValues_EM();
16327 }
16328
16329 boolean CheckEngineSnapshot()
16330 {
16331   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16332           snapshot_level_nr == level_nr);
16333 }
16334
16335
16336 /* ---------- new game button stuff ---------------------------------------- */
16337
16338 /* graphic position values for game buttons */
16339 #define GAME_BUTTON_XSIZE       30
16340 #define GAME_BUTTON_YSIZE       30
16341 #define GAME_BUTTON_XPOS        5
16342 #define GAME_BUTTON_YPOS        215
16343 #define SOUND_BUTTON_XPOS       5
16344 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16345
16346 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16347 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16348 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16349 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16350 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16351 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16352
16353 static struct
16354 {
16355   int *x, *y;
16356   int gd_x, gd_y;
16357   int gadget_id;
16358   char *infotext;
16359 } gamebutton_info[NUM_GAME_BUTTONS] =
16360 {
16361 #if 1
16362   {
16363     &game.button.stop.x,        &game.button.stop.y,
16364     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16365     GAME_CTRL_ID_STOP,
16366     "stop game"
16367   },
16368   {
16369     &game.button.pause.x,       &game.button.pause.y,
16370     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16371     GAME_CTRL_ID_PAUSE,
16372     "pause game"
16373   },
16374   {
16375     &game.button.play.x,        &game.button.play.y,
16376     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16377     GAME_CTRL_ID_PLAY,
16378     "play game"
16379   },
16380   {
16381     &game.button.sound_music.x, &game.button.sound_music.y,
16382     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16383     SOUND_CTRL_ID_MUSIC,
16384     "background music on/off"
16385   },
16386   {
16387     &game.button.sound_loops.x, &game.button.sound_loops.y,
16388     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16389     SOUND_CTRL_ID_LOOPS,
16390     "sound loops on/off"
16391   },
16392   {
16393     &game.button.sound_simple.x,&game.button.sound_simple.y,
16394     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16395     SOUND_CTRL_ID_SIMPLE,
16396     "normal sounds on/off"
16397   }
16398 #else
16399   {
16400     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16401     GAME_CTRL_ID_STOP,
16402     "stop game"
16403   },
16404   {
16405     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16406     GAME_CTRL_ID_PAUSE,
16407     "pause game"
16408   },
16409   {
16410     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16411     GAME_CTRL_ID_PLAY,
16412     "play game"
16413   },
16414   {
16415     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16416     SOUND_CTRL_ID_MUSIC,
16417     "background music on/off"
16418   },
16419   {
16420     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16421     SOUND_CTRL_ID_LOOPS,
16422     "sound loops on/off"
16423   },
16424   {
16425     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16426     SOUND_CTRL_ID_SIMPLE,
16427     "normal sounds on/off"
16428   }
16429 #endif
16430 };
16431
16432 void CreateGameButtons()
16433 {
16434   int i;
16435
16436   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16437   {
16438     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16439     struct GadgetInfo *gi;
16440     int button_type;
16441     boolean checked;
16442     unsigned long event_mask;
16443     int x, y;
16444     int gd_xoffset, gd_yoffset;
16445     int gd_x1, gd_x2, gd_y1, gd_y2;
16446     int id = i;
16447
16448     x = DX + *gamebutton_info[i].x;
16449     y = DY + *gamebutton_info[i].y;
16450     gd_xoffset = gamebutton_info[i].gd_x;
16451     gd_yoffset = gamebutton_info[i].gd_y;
16452     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16453     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16454
16455     if (id == GAME_CTRL_ID_STOP ||
16456         id == GAME_CTRL_ID_PAUSE ||
16457         id == GAME_CTRL_ID_PLAY)
16458     {
16459       button_type = GD_TYPE_NORMAL_BUTTON;
16460       checked = FALSE;
16461       event_mask = GD_EVENT_RELEASED;
16462       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16463       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16464     }
16465     else
16466     {
16467       button_type = GD_TYPE_CHECK_BUTTON;
16468       checked =
16469         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16470          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16471          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16472       event_mask = GD_EVENT_PRESSED;
16473       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16474       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16475     }
16476
16477     gi = CreateGadget(GDI_CUSTOM_ID, id,
16478                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16479 #if 1
16480                       GDI_X, x,
16481                       GDI_Y, y,
16482 #else
16483                       GDI_X, DX + gd_xoffset,
16484                       GDI_Y, DY + gd_yoffset,
16485 #endif
16486                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16487                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16488                       GDI_TYPE, button_type,
16489                       GDI_STATE, GD_BUTTON_UNPRESSED,
16490                       GDI_CHECKED, checked,
16491                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16492                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16493                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16494                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16495                       GDI_DIRECT_DRAW, FALSE,
16496                       GDI_EVENT_MASK, event_mask,
16497                       GDI_CALLBACK_ACTION, HandleGameButtons,
16498                       GDI_END);
16499
16500     if (gi == NULL)
16501       Error(ERR_EXIT, "cannot create gadget");
16502
16503     game_gadget[id] = gi;
16504   }
16505 }
16506
16507 void FreeGameButtons()
16508 {
16509   int i;
16510
16511   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16512     FreeGadget(game_gadget[i]);
16513 }
16514
16515 static void MapGameButtons()
16516 {
16517   int i;
16518
16519   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16520     MapGadget(game_gadget[i]);
16521 }
16522
16523 void UnmapGameButtons()
16524 {
16525   int i;
16526
16527   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16528     UnmapGadget(game_gadget[i]);
16529 }
16530
16531 void RedrawGameButtons()
16532 {
16533   int i;
16534
16535   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16536     RedrawGadget(game_gadget[i]);
16537 }
16538
16539 static void HandleGameButtons(struct GadgetInfo *gi)
16540 {
16541   int id = gi->custom_id;
16542
16543   if (game_status != GAME_MODE_PLAYING)
16544     return;
16545
16546   switch (id)
16547   {
16548     case GAME_CTRL_ID_STOP:
16549       if (tape.playing)
16550         TapeStop();
16551       else
16552         RequestQuitGame(TRUE);
16553       break;
16554
16555     case GAME_CTRL_ID_PAUSE:
16556       if (options.network)
16557       {
16558 #if defined(NETWORK_AVALIABLE)
16559         if (tape.pausing)
16560           SendToServer_ContinuePlaying();
16561         else
16562           SendToServer_PausePlaying();
16563 #endif
16564       }
16565       else
16566         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16567       break;
16568
16569     case GAME_CTRL_ID_PLAY:
16570       if (tape.pausing)
16571       {
16572 #if defined(NETWORK_AVALIABLE)
16573         if (options.network)
16574           SendToServer_ContinuePlaying();
16575         else
16576 #endif
16577         {
16578           tape.pausing = FALSE;
16579           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16580         }
16581       }
16582       break;
16583
16584     case SOUND_CTRL_ID_MUSIC:
16585       if (setup.sound_music)
16586       { 
16587         setup.sound_music = FALSE;
16588         FadeMusic();
16589       }
16590       else if (audio.music_available)
16591       { 
16592         setup.sound = setup.sound_music = TRUE;
16593
16594         SetAudioMode(setup.sound);
16595
16596         PlayLevelMusic();
16597       }
16598       break;
16599
16600     case SOUND_CTRL_ID_LOOPS:
16601       if (setup.sound_loops)
16602         setup.sound_loops = FALSE;
16603       else if (audio.loops_available)
16604       {
16605         setup.sound = setup.sound_loops = TRUE;
16606         SetAudioMode(setup.sound);
16607       }
16608       break;
16609
16610     case SOUND_CTRL_ID_SIMPLE:
16611       if (setup.sound_simple)
16612         setup.sound_simple = FALSE;
16613       else if (audio.sound_available)
16614       {
16615         setup.sound = setup.sound_simple = TRUE;
16616         SetAudioMode(setup.sound);
16617       }
16618       break;
16619
16620     default:
16621       break;
16622   }
16623 }