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