rnd-20080205-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)
1795       player->killed = FALSE;   /* 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_PLAYER_1;
3455       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
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
3711     player->action = 0;
3712     player->effective_action = 0;
3713     player->programmed_action = 0;
3714
3715     player->score = 0;
3716     player->score_final = 0;
3717
3718     player->gems_still_needed = level.gems_needed;
3719     player->sokobanfields_still_needed = 0;
3720     player->lights_still_needed = 0;
3721     player->friends_still_needed = 0;
3722
3723     for (j = 0; j < MAX_NUM_KEYS; j++)
3724       player->key[j] = FALSE;
3725
3726     player->num_white_keys = 0;
3727
3728     player->dynabomb_count = 0;
3729     player->dynabomb_size = 1;
3730     player->dynabombs_left = 0;
3731     player->dynabomb_xl = FALSE;
3732
3733     player->MovDir = initial_move_dir;
3734     player->MovPos = 0;
3735     player->GfxPos = 0;
3736     player->GfxDir = initial_move_dir;
3737     player->GfxAction = ACTION_DEFAULT;
3738     player->Frame = 0;
3739     player->StepFrame = 0;
3740
3741     player->initial_element = player->element_nr;
3742     player->artwork_element =
3743       (level.use_artwork_element[i] ? level.artwork_element[i] :
3744        player->element_nr);
3745     player->use_murphy = FALSE;
3746
3747     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3748     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3749
3750     player->gravity = level.initial_player_gravity[i];
3751
3752     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3753
3754     player->actual_frame_counter = 0;
3755
3756     player->step_counter = 0;
3757
3758     player->last_move_dir = initial_move_dir;
3759
3760     player->is_active = FALSE;
3761
3762     player->is_waiting = FALSE;
3763     player->is_moving = FALSE;
3764     player->is_auto_moving = FALSE;
3765     player->is_digging = FALSE;
3766     player->is_snapping = FALSE;
3767     player->is_collecting = FALSE;
3768     player->is_pushing = FALSE;
3769     player->is_switching = FALSE;
3770     player->is_dropping = FALSE;
3771     player->is_dropping_pressed = FALSE;
3772
3773     player->is_bored = FALSE;
3774     player->is_sleeping = FALSE;
3775
3776     player->frame_counter_bored = -1;
3777     player->frame_counter_sleeping = -1;
3778
3779     player->anim_delay_counter = 0;
3780     player->post_delay_counter = 0;
3781
3782     player->dir_waiting = initial_move_dir;
3783     player->action_waiting = ACTION_DEFAULT;
3784     player->last_action_waiting = ACTION_DEFAULT;
3785     player->special_action_bored = ACTION_DEFAULT;
3786     player->special_action_sleeping = ACTION_DEFAULT;
3787
3788     player->switch_x = -1;
3789     player->switch_y = -1;
3790
3791     player->drop_x = -1;
3792     player->drop_y = -1;
3793
3794     player->show_envelope = 0;
3795
3796     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3797
3798     player->push_delay       = -1;      /* initialized when pushing starts */
3799     player->push_delay_value = game.initial_push_delay_value;
3800
3801     player->drop_delay = 0;
3802     player->drop_pressed_delay = 0;
3803
3804     player->last_jx = -1;
3805     player->last_jy = -1;
3806     player->jx = -1;
3807     player->jy = -1;
3808
3809     player->shield_normal_time_left = 0;
3810     player->shield_deadly_time_left = 0;
3811
3812     player->inventory_infinite_element = EL_UNDEFINED;
3813     player->inventory_size = 0;
3814
3815     if (level.use_initial_inventory[i])
3816     {
3817       for (j = 0; j < level.initial_inventory_size[i]; j++)
3818       {
3819         int element = level.initial_inventory_content[i][j];
3820         int collect_count = element_info[element].collect_count_initial;
3821         int k;
3822
3823         if (!IS_CUSTOM_ELEMENT(element))
3824           collect_count = 1;
3825
3826         if (collect_count == 0)
3827           player->inventory_infinite_element = element;
3828         else
3829           for (k = 0; k < collect_count; k++)
3830             if (player->inventory_size < MAX_INVENTORY_SIZE)
3831               player->inventory_element[player->inventory_size++] = element;
3832       }
3833     }
3834
3835     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3836     SnapField(player, 0, 0);
3837
3838     player->LevelSolved = FALSE;
3839     player->GameOver = FALSE;
3840
3841     player->LevelSolved_GameWon = FALSE;
3842     player->LevelSolved_GameEnd = FALSE;
3843     player->LevelSolved_PanelOff = FALSE;
3844     player->LevelSolved_SaveTape = FALSE;
3845     player->LevelSolved_SaveScore = FALSE;
3846     player->LevelSolved_CountingTime = 0;
3847     player->LevelSolved_CountingScore = 0;
3848   }
3849
3850   network_player_action_received = FALSE;
3851
3852 #if defined(NETWORK_AVALIABLE)
3853   /* initial null action */
3854   if (network_playing)
3855     SendToServer_MovePlayer(MV_NONE);
3856 #endif
3857
3858   ZX = ZY = -1;
3859   ExitX = ExitY = -1;
3860
3861   FrameCounter = 0;
3862   TimeFrames = 0;
3863   TimePlayed = 0;
3864   TimeLeft = level.time;
3865   TapeTime = 0;
3866
3867   ScreenMovDir = MV_NONE;
3868   ScreenMovPos = 0;
3869   ScreenGfxPos = 0;
3870
3871   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3872
3873   AllPlayersGone = FALSE;
3874
3875   game.yamyam_content_nr = 0;
3876   game.robot_wheel_active = FALSE;
3877   game.magic_wall_active = FALSE;
3878   game.magic_wall_time_left = 0;
3879   game.light_time_left = 0;
3880   game.timegate_time_left = 0;
3881   game.switchgate_pos = 0;
3882   game.wind_direction = level.wind_direction_initial;
3883
3884 #if !USE_PLAYER_GRAVITY
3885   game.gravity = FALSE;
3886   game.explosions_delayed = TRUE;
3887 #endif
3888
3889   game.lenses_time_left = 0;
3890   game.magnify_time_left = 0;
3891
3892   game.ball_state = level.ball_state_initial;
3893   game.ball_content_nr = 0;
3894
3895   game.envelope_active = FALSE;
3896
3897   /* set focus to local player for network games, else to all players */
3898   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3899   game.centered_player_nr_next = game.centered_player_nr;
3900   game.set_centered_player = FALSE;
3901
3902   if (network_playing && tape.recording)
3903   {
3904     /* store client dependent player focus when recording network games */
3905     tape.centered_player_nr_next = game.centered_player_nr_next;
3906     tape.set_centered_player = TRUE;
3907   }
3908
3909   for (i = 0; i < NUM_BELTS; i++)
3910   {
3911     game.belt_dir[i] = MV_NONE;
3912     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3913   }
3914
3915   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3916     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3917
3918   SCAN_PLAYFIELD(x, y)
3919   {
3920     Feld[x][y] = level.field[x][y];
3921     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3922     ChangeDelay[x][y] = 0;
3923     ChangePage[x][y] = -1;
3924 #if USE_NEW_CUSTOM_VALUE
3925     CustomValue[x][y] = 0;              /* initialized in InitField() */
3926 #endif
3927     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3928     AmoebaNr[x][y] = 0;
3929     WasJustMoving[x][y] = 0;
3930     WasJustFalling[x][y] = 0;
3931     CheckCollision[x][y] = 0;
3932     CheckImpact[x][y] = 0;
3933     Stop[x][y] = FALSE;
3934     Pushed[x][y] = FALSE;
3935
3936     ChangeCount[x][y] = 0;
3937     ChangeEvent[x][y] = -1;
3938
3939     ExplodePhase[x][y] = 0;
3940     ExplodeDelay[x][y] = 0;
3941     ExplodeField[x][y] = EX_TYPE_NONE;
3942
3943     RunnerVisit[x][y] = 0;
3944     PlayerVisit[x][y] = 0;
3945
3946     GfxFrame[x][y] = 0;
3947     GfxRandom[x][y] = INIT_GFX_RANDOM();
3948     GfxElement[x][y] = EL_UNDEFINED;
3949     GfxAction[x][y] = ACTION_DEFAULT;
3950     GfxDir[x][y] = MV_NONE;
3951     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3952   }
3953
3954   SCAN_PLAYFIELD(x, y)
3955   {
3956     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3957       emulate_bd = FALSE;
3958     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3959       emulate_sb = FALSE;
3960     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3961       emulate_sp = FALSE;
3962
3963     InitField(x, y, TRUE);
3964
3965     ResetGfxAnimation(x, y);
3966   }
3967
3968   InitBeltMovement();
3969
3970   for (i = 0; i < MAX_PLAYERS; i++)
3971   {
3972     struct PlayerInfo *player = &stored_player[i];
3973
3974     /* set number of special actions for bored and sleeping animation */
3975     player->num_special_action_bored =
3976       get_num_special_action(player->artwork_element,
3977                              ACTION_BORING_1, ACTION_BORING_LAST);
3978     player->num_special_action_sleeping =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3981   }
3982
3983   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3984                     emulate_sb ? EMU_SOKOBAN :
3985                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3986
3987 #if USE_NEW_ALL_SLIPPERY
3988   /* initialize type of slippery elements */
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       /* default: elements slip down either to the left or right randomly */
3994       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3995
3996       /* SP style elements prefer to slip down on the left side */
3997       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3998         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3999
4000       /* BD style elements prefer to slip down on the left side */
4001       if (game.emulation == EMU_BOULDERDASH)
4002         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4003     }
4004   }
4005 #endif
4006
4007   /* initialize explosion and ignition delay */
4008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4009   {
4010     if (!IS_CUSTOM_ELEMENT(i))
4011     {
4012       int num_phase = 8;
4013       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4014                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4015                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4016       int last_phase = (num_phase + 1) * delay;
4017       int half_phase = (num_phase / 2) * delay;
4018
4019       element_info[i].explosion_delay = last_phase - 1;
4020       element_info[i].ignition_delay = half_phase;
4021
4022       if (i == EL_BLACK_ORB)
4023         element_info[i].ignition_delay = 1;
4024     }
4025
4026 #if 0
4027     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4028       element_info[i].explosion_delay = 1;
4029
4030     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4031       element_info[i].ignition_delay = 1;
4032 #endif
4033   }
4034
4035   /* correct non-moving belts to start moving left */
4036   for (i = 0; i < NUM_BELTS; i++)
4037     if (game.belt_dir[i] == MV_NONE)
4038       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4039
4040   /* check if any connected player was not found in playfield */
4041   for (i = 0; i < MAX_PLAYERS; i++)
4042   {
4043     struct PlayerInfo *player = &stored_player[i];
4044
4045     if (player->connected && !player->present)
4046     {
4047       for (j = 0; j < MAX_PLAYERS; j++)
4048       {
4049         struct PlayerInfo *some_player = &stored_player[j];
4050         int jx = some_player->jx, jy = some_player->jy;
4051
4052         /* assign first free player found that is present in the playfield */
4053         if (some_player->present && !some_player->connected)
4054         {
4055           player->present = TRUE;
4056           player->active = TRUE;
4057
4058           some_player->present = FALSE;
4059           some_player->active = FALSE;
4060
4061           player->initial_element = some_player->initial_element;
4062           player->artwork_element = some_player->artwork_element;
4063
4064           player->block_last_field       = some_player->block_last_field;
4065           player->block_delay_adjustment = some_player->block_delay_adjustment;
4066
4067           StorePlayer[jx][jy] = player->element_nr;
4068           player->jx = player->last_jx = jx;
4069           player->jy = player->last_jy = jy;
4070
4071           break;
4072         }
4073       }
4074     }
4075   }
4076
4077   if (tape.playing)
4078   {
4079     /* when playing a tape, eliminate all players who do not participate */
4080
4081     for (i = 0; i < MAX_PLAYERS; i++)
4082     {
4083       if (stored_player[i].active && !tape.player_participates[i])
4084       {
4085         struct PlayerInfo *player = &stored_player[i];
4086         int jx = player->jx, jy = player->jy;
4087
4088         player->active = FALSE;
4089         StorePlayer[jx][jy] = 0;
4090         Feld[jx][jy] = EL_EMPTY;
4091       }
4092     }
4093   }
4094   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4095   {
4096     /* when in single player mode, eliminate all but the first active player */
4097
4098     for (i = 0; i < MAX_PLAYERS; i++)
4099     {
4100       if (stored_player[i].active)
4101       {
4102         for (j = i + 1; j < MAX_PLAYERS; j++)
4103         {
4104           if (stored_player[j].active)
4105           {
4106             struct PlayerInfo *player = &stored_player[j];
4107             int jx = player->jx, jy = player->jy;
4108
4109             player->active = FALSE;
4110             player->present = FALSE;
4111
4112             StorePlayer[jx][jy] = 0;
4113             Feld[jx][jy] = EL_EMPTY;
4114           }
4115         }
4116       }
4117     }
4118   }
4119
4120   /* when recording the game, store which players take part in the game */
4121   if (tape.recording)
4122   {
4123     for (i = 0; i < MAX_PLAYERS; i++)
4124       if (stored_player[i].active)
4125         tape.player_participates[i] = TRUE;
4126   }
4127
4128   if (options.debug)
4129   {
4130     for (i = 0; i < MAX_PLAYERS; i++)
4131     {
4132       struct PlayerInfo *player = &stored_player[i];
4133
4134       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4135              i+1,
4136              player->present,
4137              player->connected,
4138              player->active);
4139       if (local_player == player)
4140         printf("Player  %d is local player.\n", i+1);
4141     }
4142   }
4143
4144   if (BorderElement == EL_EMPTY)
4145   {
4146     SBX_Left = 0;
4147     SBX_Right = lev_fieldx - SCR_FIELDX;
4148     SBY_Upper = 0;
4149     SBY_Lower = lev_fieldy - SCR_FIELDY;
4150   }
4151   else
4152   {
4153     SBX_Left = -1;
4154     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4155     SBY_Upper = -1;
4156     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4157   }
4158
4159   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4161
4162   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4164
4165   /* if local player not found, look for custom element that might create
4166      the player (make some assumptions about the right custom element) */
4167   if (!local_player->present)
4168   {
4169     int start_x = 0, start_y = 0;
4170     int found_rating = 0;
4171     int found_element = EL_UNDEFINED;
4172     int player_nr = local_player->index_nr;
4173
4174     SCAN_PLAYFIELD(x, y)
4175     {
4176       int element = Feld[x][y];
4177       int content;
4178       int xx, yy;
4179       boolean is_player;
4180
4181       if (level.use_start_element[player_nr] &&
4182           level.start_element[player_nr] == element &&
4183           found_rating < 4)
4184       {
4185         start_x = x;
4186         start_y = y;
4187
4188         found_rating = 4;
4189         found_element = element;
4190       }
4191
4192       if (!IS_CUSTOM_ELEMENT(element))
4193         continue;
4194
4195       if (CAN_CHANGE(element))
4196       {
4197         for (i = 0; i < element_info[element].num_change_pages; i++)
4198         {
4199           /* check for player created from custom element as single target */
4200           content = element_info[element].change_page[i].target_element;
4201           is_player = ELEM_IS_PLAYER(content);
4202
4203           if (is_player && (found_rating < 3 ||
4204                             (found_rating == 3 && element < found_element)))
4205           {
4206             start_x = x;
4207             start_y = y;
4208
4209             found_rating = 3;
4210             found_element = element;
4211           }
4212         }
4213       }
4214
4215       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4216       {
4217         /* check for player created from custom element as explosion content */
4218         content = element_info[element].content.e[xx][yy];
4219         is_player = ELEM_IS_PLAYER(content);
4220
4221         if (is_player && (found_rating < 2 ||
4222                           (found_rating == 2 && element < found_element)))
4223         {
4224           start_x = x + xx - 1;
4225           start_y = y + yy - 1;
4226
4227           found_rating = 2;
4228           found_element = element;
4229         }
4230
4231         if (!CAN_CHANGE(element))
4232           continue;
4233
4234         for (i = 0; i < element_info[element].num_change_pages; i++)
4235         {
4236           /* check for player created from custom element as extended target */
4237           content =
4238             element_info[element].change_page[i].target_content.e[xx][yy];
4239
4240           is_player = ELEM_IS_PLAYER(content);
4241
4242           if (is_player && (found_rating < 1 ||
4243                             (found_rating == 1 && element < found_element)))
4244           {
4245             start_x = x + xx - 1;
4246             start_y = y + yy - 1;
4247
4248             found_rating = 1;
4249             found_element = element;
4250           }
4251         }
4252       }
4253     }
4254
4255     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4256                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4257                 start_x - MIDPOSX);
4258
4259     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4260                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4261                 start_y - MIDPOSY);
4262   }
4263   else
4264   {
4265     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4266                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4267                 local_player->jx - MIDPOSX);
4268
4269     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4270                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4271                 local_player->jy - MIDPOSY);
4272   }
4273
4274 #if 0
4275   /* do not use PLAYING mask for fading out from main screen */
4276   game_status = GAME_MODE_MAIN;
4277 #endif
4278
4279   StopAnimation();
4280
4281   if (!game.restart_level)
4282     CloseDoor(DOOR_CLOSE_1);
4283
4284 #if 1
4285   if (level_editor_test_game)
4286     FadeSkipNextFadeIn();
4287   else
4288     FadeSetEnterScreen();
4289 #else
4290   if (level_editor_test_game)
4291     fading = fading_none;
4292   else
4293     fading = menu.destination;
4294 #endif
4295
4296 #if 1
4297   FadeOut(REDRAW_FIELD);
4298 #else
4299   if (do_fading)
4300     FadeOut(REDRAW_FIELD);
4301 #endif
4302
4303 #if 0
4304   game_status = GAME_MODE_PLAYING;
4305 #endif
4306
4307   /* !!! FIX THIS (START) !!! */
4308   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4309   {
4310     InitGameEngine_EM();
4311
4312     /* blit playfield from scroll buffer to normal back buffer for fading in */
4313     BlitScreenToBitmap_EM(backbuffer);
4314   }
4315   else
4316   {
4317     DrawLevel();
4318     DrawAllPlayers();
4319
4320     /* after drawing the level, correct some elements */
4321     if (game.timegate_time_left == 0)
4322       CloseAllOpenTimegates();
4323
4324     /* blit playfield from scroll buffer to normal back buffer for fading in */
4325     if (setup.soft_scrolling)
4326       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4327
4328     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4329   }
4330   /* !!! FIX THIS (END) !!! */
4331
4332 #if 1
4333   FadeIn(REDRAW_FIELD);
4334 #else
4335   if (do_fading)
4336     FadeIn(REDRAW_FIELD);
4337
4338   BackToFront();
4339 #endif
4340
4341   if (!game.restart_level)
4342   {
4343     /* copy default game door content to main double buffer */
4344     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4345                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4346   }
4347
4348   SetPanelBackground();
4349   SetDrawBackgroundMask(REDRAW_DOOR_1);
4350
4351 #if 1
4352   UpdateAndDisplayGameControlValues();
4353 #else
4354   UpdateGameDoorValues();
4355   DrawGameDoorValues();
4356 #endif
4357
4358   if (!game.restart_level)
4359   {
4360     UnmapGameButtons();
4361     UnmapTapeButtons();
4362     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4363     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4364     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4365     MapGameButtons();
4366     MapTapeButtons();
4367
4368     /* copy actual game door content to door double buffer for OpenDoor() */
4369     BlitBitmap(drawto, bitmap_db_door,
4370                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4371
4372     OpenDoor(DOOR_OPEN_ALL);
4373
4374     PlaySound(SND_GAME_STARTING);
4375
4376     if (setup.sound_music)
4377       PlayLevelMusic();
4378
4379     KeyboardAutoRepeatOffUnlessAutoplay();
4380
4381     if (options.debug)
4382     {
4383       for (i = 0; i < MAX_PLAYERS; i++)
4384         printf("Player %d %sactive.\n",
4385                i + 1, (stored_player[i].active ? "" : "not "));
4386     }
4387   }
4388
4389 #if 1
4390   UnmapAllGadgets();
4391
4392   MapGameButtons();
4393   MapTapeButtons();
4394 #endif
4395
4396   game.restart_level = FALSE;
4397 }
4398
4399 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4400 {
4401   /* this is used for non-R'n'D game engines to update certain engine values */
4402
4403   /* needed to determine if sounds are played within the visible screen area */
4404   scroll_x = actual_scroll_x;
4405   scroll_y = actual_scroll_y;
4406 }
4407
4408 void InitMovDir(int x, int y)
4409 {
4410   int i, element = Feld[x][y];
4411   static int xy[4][2] =
4412   {
4413     {  0, +1 },
4414     { +1,  0 },
4415     {  0, -1 },
4416     { -1,  0 }
4417   };
4418   static int direction[3][4] =
4419   {
4420     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4421     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4422     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4423   };
4424
4425   switch (element)
4426   {
4427     case EL_BUG_RIGHT:
4428     case EL_BUG_UP:
4429     case EL_BUG_LEFT:
4430     case EL_BUG_DOWN:
4431       Feld[x][y] = EL_BUG;
4432       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4433       break;
4434
4435     case EL_SPACESHIP_RIGHT:
4436     case EL_SPACESHIP_UP:
4437     case EL_SPACESHIP_LEFT:
4438     case EL_SPACESHIP_DOWN:
4439       Feld[x][y] = EL_SPACESHIP;
4440       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4441       break;
4442
4443     case EL_BD_BUTTERFLY_RIGHT:
4444     case EL_BD_BUTTERFLY_UP:
4445     case EL_BD_BUTTERFLY_LEFT:
4446     case EL_BD_BUTTERFLY_DOWN:
4447       Feld[x][y] = EL_BD_BUTTERFLY;
4448       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4449       break;
4450
4451     case EL_BD_FIREFLY_RIGHT:
4452     case EL_BD_FIREFLY_UP:
4453     case EL_BD_FIREFLY_LEFT:
4454     case EL_BD_FIREFLY_DOWN:
4455       Feld[x][y] = EL_BD_FIREFLY;
4456       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4457       break;
4458
4459     case EL_PACMAN_RIGHT:
4460     case EL_PACMAN_UP:
4461     case EL_PACMAN_LEFT:
4462     case EL_PACMAN_DOWN:
4463       Feld[x][y] = EL_PACMAN;
4464       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4465       break;
4466
4467     case EL_YAMYAM_LEFT:
4468     case EL_YAMYAM_RIGHT:
4469     case EL_YAMYAM_UP:
4470     case EL_YAMYAM_DOWN:
4471       Feld[x][y] = EL_YAMYAM;
4472       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4473       break;
4474
4475     case EL_SP_SNIKSNAK:
4476       MovDir[x][y] = MV_UP;
4477       break;
4478
4479     case EL_SP_ELECTRON:
4480       MovDir[x][y] = MV_LEFT;
4481       break;
4482
4483     case EL_MOLE_LEFT:
4484     case EL_MOLE_RIGHT:
4485     case EL_MOLE_UP:
4486     case EL_MOLE_DOWN:
4487       Feld[x][y] = EL_MOLE;
4488       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4489       break;
4490
4491     default:
4492       if (IS_CUSTOM_ELEMENT(element))
4493       {
4494         struct ElementInfo *ei = &element_info[element];
4495         int move_direction_initial = ei->move_direction_initial;
4496         int move_pattern = ei->move_pattern;
4497
4498         if (move_direction_initial == MV_START_PREVIOUS)
4499         {
4500           if (MovDir[x][y] != MV_NONE)
4501             return;
4502
4503           move_direction_initial = MV_START_AUTOMATIC;
4504         }
4505
4506         if (move_direction_initial == MV_START_RANDOM)
4507           MovDir[x][y] = 1 << RND(4);
4508         else if (move_direction_initial & MV_ANY_DIRECTION)
4509           MovDir[x][y] = move_direction_initial;
4510         else if (move_pattern == MV_ALL_DIRECTIONS ||
4511                  move_pattern == MV_TURNING_LEFT ||
4512                  move_pattern == MV_TURNING_RIGHT ||
4513                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4514                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4515                  move_pattern == MV_TURNING_RANDOM)
4516           MovDir[x][y] = 1 << RND(4);
4517         else if (move_pattern == MV_HORIZONTAL)
4518           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4519         else if (move_pattern == MV_VERTICAL)
4520           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4521         else if (move_pattern & MV_ANY_DIRECTION)
4522           MovDir[x][y] = element_info[element].move_pattern;
4523         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4524                  move_pattern == MV_ALONG_RIGHT_SIDE)
4525         {
4526           /* use random direction as default start direction */
4527           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4528             MovDir[x][y] = 1 << RND(4);
4529
4530           for (i = 0; i < NUM_DIRECTIONS; i++)
4531           {
4532             int x1 = x + xy[i][0];
4533             int y1 = y + xy[i][1];
4534
4535             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4536             {
4537               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4538                 MovDir[x][y] = direction[0][i];
4539               else
4540                 MovDir[x][y] = direction[1][i];
4541
4542               break;
4543             }
4544           }
4545         }                
4546       }
4547       else
4548       {
4549         MovDir[x][y] = 1 << RND(4);
4550
4551         if (element != EL_BUG &&
4552             element != EL_SPACESHIP &&
4553             element != EL_BD_BUTTERFLY &&
4554             element != EL_BD_FIREFLY)
4555           break;
4556
4557         for (i = 0; i < NUM_DIRECTIONS; i++)
4558         {
4559           int x1 = x + xy[i][0];
4560           int y1 = y + xy[i][1];
4561
4562           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4563           {
4564             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4565             {
4566               MovDir[x][y] = direction[0][i];
4567               break;
4568             }
4569             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4570                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4571             {
4572               MovDir[x][y] = direction[1][i];
4573               break;
4574             }
4575           }
4576         }
4577       }
4578       break;
4579   }
4580
4581   GfxDir[x][y] = MovDir[x][y];
4582 }
4583
4584 void InitAmoebaNr(int x, int y)
4585 {
4586   int i;
4587   int group_nr = AmoebeNachbarNr(x, y);
4588
4589   if (group_nr == 0)
4590   {
4591     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4592     {
4593       if (AmoebaCnt[i] == 0)
4594       {
4595         group_nr = i;
4596         break;
4597       }
4598     }
4599   }
4600
4601   AmoebaNr[x][y] = group_nr;
4602   AmoebaCnt[group_nr]++;
4603   AmoebaCnt2[group_nr]++;
4604 }
4605
4606 static void PlayerWins(struct PlayerInfo *player)
4607 {
4608   player->LevelSolved = TRUE;
4609   player->GameOver = TRUE;
4610
4611   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4612                          level.native_em_level->lev->score : player->score);
4613
4614   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4615   player->LevelSolved_CountingScore = player->score_final;
4616 }
4617
4618 void GameWon()
4619 {
4620   static int time, time_final;
4621   static int score, score_final;
4622   static int game_over_delay_1 = 0;
4623   static int game_over_delay_2 = 0;
4624   int game_over_delay_value_1 = 50;
4625   int game_over_delay_value_2 = 50;
4626
4627   if (!local_player->LevelSolved_GameWon)
4628   {
4629     int i;
4630
4631     /* do not start end game actions before the player stops moving (to exit) */
4632     if (local_player->MovPos)
4633       return;
4634
4635     local_player->LevelSolved_GameWon = TRUE;
4636     local_player->LevelSolved_SaveTape = tape.recording;
4637     local_player->LevelSolved_SaveScore = !tape.playing;
4638
4639     if (tape.auto_play)         /* tape might already be stopped here */
4640       tape.auto_play_level_solved = TRUE;
4641
4642 #if 1
4643     TapeStop();
4644 #endif
4645
4646     game_over_delay_1 = game_over_delay_value_1;
4647     game_over_delay_2 = game_over_delay_value_2;
4648
4649     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4650     score = score_final = local_player->score_final;
4651
4652     if (TimeLeft > 0)
4653     {
4654       time_final = 0;
4655       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4656     }
4657     else if (level.time == 0 && TimePlayed < 999)
4658     {
4659       time_final = 999;
4660       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4661     }
4662
4663     local_player->score_final = score_final;
4664
4665     if (level_editor_test_game)
4666     {
4667       time = time_final;
4668       score = score_final;
4669
4670 #if 1
4671       local_player->LevelSolved_CountingTime = time;
4672       local_player->LevelSolved_CountingScore = score;
4673
4674       game_panel_controls[GAME_PANEL_TIME].value = time;
4675       game_panel_controls[GAME_PANEL_SCORE].value = score;
4676
4677       DisplayGameControlValues();
4678 #else
4679       DrawGameValue_Time(time);
4680       DrawGameValue_Score(score);
4681 #endif
4682     }
4683
4684     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4685     {
4686       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4687       {
4688         /* close exit door after last player */
4689         if ((AllPlayersGone &&
4690              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4691               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4692               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4693             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4694             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4695         {
4696           int element = Feld[ExitX][ExitY];
4697
4698 #if 0
4699           if (element == EL_EM_EXIT_OPEN ||
4700               element == EL_EM_STEEL_EXIT_OPEN)
4701           {
4702             Bang(ExitX, ExitY);
4703           }
4704           else
4705 #endif
4706           {
4707             Feld[ExitX][ExitY] =
4708               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4709                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4710                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4711                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4712                EL_EM_STEEL_EXIT_CLOSING);
4713
4714             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4715           }
4716         }
4717
4718         /* player disappears */
4719         DrawLevelField(ExitX, ExitY);
4720       }
4721
4722       for (i = 0; i < MAX_PLAYERS; i++)
4723       {
4724         struct PlayerInfo *player = &stored_player[i];
4725
4726         if (player->present)
4727         {
4728           RemovePlayer(player);
4729
4730           /* player disappears */
4731           DrawLevelField(player->jx, player->jy);
4732         }
4733       }
4734     }
4735
4736     PlaySound(SND_GAME_WINNING);
4737   }
4738
4739   if (game_over_delay_1 > 0)
4740   {
4741     game_over_delay_1--;
4742
4743     return;
4744   }
4745
4746   if (time != time_final)
4747   {
4748     int time_to_go = ABS(time_final - time);
4749     int time_count_dir = (time < time_final ? +1 : -1);
4750     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4751
4752     time  += time_count_steps * time_count_dir;
4753     score += time_count_steps * level.score[SC_TIME_BONUS];
4754
4755 #if 1
4756     local_player->LevelSolved_CountingTime = time;
4757     local_player->LevelSolved_CountingScore = score;
4758
4759     game_panel_controls[GAME_PANEL_TIME].value = time;
4760     game_panel_controls[GAME_PANEL_SCORE].value = score;
4761
4762     DisplayGameControlValues();
4763 #else
4764     DrawGameValue_Time(time);
4765     DrawGameValue_Score(score);
4766 #endif
4767
4768     if (time == time_final)
4769       StopSound(SND_GAME_LEVELTIME_BONUS);
4770     else if (setup.sound_loops)
4771       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4772     else
4773       PlaySound(SND_GAME_LEVELTIME_BONUS);
4774
4775     return;
4776   }
4777
4778   local_player->LevelSolved_PanelOff = TRUE;
4779
4780   if (game_over_delay_2 > 0)
4781   {
4782     game_over_delay_2--;
4783
4784     return;
4785   }
4786
4787 #if 1
4788   GameEnd();
4789 #endif
4790 }
4791
4792 void GameEnd()
4793 {
4794   int hi_pos;
4795   boolean raise_level = FALSE;
4796
4797   local_player->LevelSolved_GameEnd = TRUE;
4798
4799   CloseDoor(DOOR_CLOSE_1);
4800
4801   if (local_player->LevelSolved_SaveTape)
4802   {
4803 #if 0
4804     TapeStop();
4805 #endif
4806
4807 #if 1
4808     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4809 #else
4810     SaveTape(tape.level_nr);            /* ask to save tape */
4811 #endif
4812   }
4813
4814   if (level_editor_test_game)
4815   {
4816     game_status = GAME_MODE_MAIN;
4817
4818 #if 1
4819     DrawAndFadeInMainMenu(REDRAW_FIELD);
4820 #else
4821     DrawMainMenu();
4822 #endif
4823
4824     return;
4825   }
4826
4827   if (!local_player->LevelSolved_SaveScore)
4828   {
4829 #if 1
4830     FadeOut(REDRAW_FIELD);
4831 #endif
4832
4833     game_status = GAME_MODE_MAIN;
4834
4835     DrawAndFadeInMainMenu(REDRAW_FIELD);
4836
4837     return;
4838   }
4839
4840   if (level_nr == leveldir_current->handicap_level)
4841   {
4842     leveldir_current->handicap_level++;
4843     SaveLevelSetup_SeriesInfo();
4844   }
4845
4846   if (level_nr < leveldir_current->last_level)
4847     raise_level = TRUE;                 /* advance to next level */
4848
4849   if ((hi_pos = NewHiScore()) >= 0) 
4850   {
4851     game_status = GAME_MODE_SCORES;
4852
4853     DrawHallOfFame(hi_pos);
4854
4855     if (raise_level)
4856     {
4857       level_nr++;
4858       TapeErase();
4859     }
4860   }
4861   else
4862   {
4863 #if 1
4864     FadeOut(REDRAW_FIELD);
4865 #endif
4866
4867     game_status = GAME_MODE_MAIN;
4868
4869     if (raise_level)
4870     {
4871       level_nr++;
4872       TapeErase();
4873     }
4874
4875     DrawAndFadeInMainMenu(REDRAW_FIELD);
4876   }
4877 }
4878
4879 int NewHiScore()
4880 {
4881   int k, l;
4882   int position = -1;
4883
4884   LoadScore(level_nr);
4885
4886   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4887       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4888     return -1;
4889
4890   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4891   {
4892     if (local_player->score_final > highscore[k].Score)
4893     {
4894       /* player has made it to the hall of fame */
4895
4896       if (k < MAX_SCORE_ENTRIES - 1)
4897       {
4898         int m = MAX_SCORE_ENTRIES - 1;
4899
4900 #ifdef ONE_PER_NAME
4901         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4902           if (strEqual(setup.player_name, highscore[l].Name))
4903             m = l;
4904         if (m == k)     /* player's new highscore overwrites his old one */
4905           goto put_into_list;
4906 #endif
4907
4908         for (l = m; l > k; l--)
4909         {
4910           strcpy(highscore[l].Name, highscore[l - 1].Name);
4911           highscore[l].Score = highscore[l - 1].Score;
4912         }
4913       }
4914
4915 #ifdef ONE_PER_NAME
4916       put_into_list:
4917 #endif
4918       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4919       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4920       highscore[k].Score = local_player->score_final; 
4921       position = k;
4922       break;
4923     }
4924
4925 #ifdef ONE_PER_NAME
4926     else if (!strncmp(setup.player_name, highscore[k].Name,
4927                       MAX_PLAYER_NAME_LEN))
4928       break;    /* player already there with a higher score */
4929 #endif
4930
4931   }
4932
4933   if (position >= 0) 
4934     SaveScore(level_nr);
4935
4936   return position;
4937 }
4938
4939 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4940 {
4941   int element = Feld[x][y];
4942   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4943   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4944   int horiz_move = (dx != 0);
4945   int sign = (horiz_move ? dx : dy);
4946   int step = sign * element_info[element].move_stepsize;
4947
4948   /* special values for move stepsize for spring and things on conveyor belt */
4949   if (horiz_move)
4950   {
4951     if (CAN_FALL(element) &&
4952         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4953       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4954     else if (element == EL_SPRING)
4955       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4956   }
4957
4958   return step;
4959 }
4960
4961 inline static int getElementMoveStepsize(int x, int y)
4962 {
4963   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4964 }
4965
4966 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4967 {
4968   if (player->GfxAction != action || player->GfxDir != dir)
4969   {
4970 #if 0
4971     printf("Player frame reset! (%d => %d, %d => %d)\n",
4972            player->GfxAction, action, player->GfxDir, dir);
4973 #endif
4974
4975     player->GfxAction = action;
4976     player->GfxDir = dir;
4977     player->Frame = 0;
4978     player->StepFrame = 0;
4979   }
4980 }
4981
4982 #if USE_GFX_RESET_GFX_ANIMATION
4983 static void ResetGfxFrame(int x, int y, boolean redraw)
4984 {
4985   int element = Feld[x][y];
4986   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4987   int last_gfx_frame = GfxFrame[x][y];
4988
4989   if (graphic_info[graphic].anim_global_sync)
4990     GfxFrame[x][y] = FrameCounter;
4991   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4992     GfxFrame[x][y] = CustomValue[x][y];
4993   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4994     GfxFrame[x][y] = element_info[element].collect_score;
4995   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4996     GfxFrame[x][y] = ChangeDelay[x][y];
4997
4998   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4999     DrawLevelGraphicAnimation(x, y, graphic);
5000 }
5001 #endif
5002
5003 static void ResetGfxAnimation(int x, int y)
5004 {
5005   GfxAction[x][y] = ACTION_DEFAULT;
5006   GfxDir[x][y] = MovDir[x][y];
5007   GfxFrame[x][y] = 0;
5008
5009 #if USE_GFX_RESET_GFX_ANIMATION
5010   ResetGfxFrame(x, y, FALSE);
5011 #endif
5012 }
5013
5014 static void ResetRandomAnimationValue(int x, int y)
5015 {
5016   GfxRandom[x][y] = INIT_GFX_RANDOM();
5017 }
5018
5019 void InitMovingField(int x, int y, int direction)
5020 {
5021   int element = Feld[x][y];
5022   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5023   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5024   int newx = x + dx;
5025   int newy = y + dy;
5026   boolean is_moving_before, is_moving_after;
5027 #if 0
5028   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5029 #endif
5030
5031   /* check if element was/is moving or being moved before/after mode change */
5032 #if 1
5033 #if 1
5034   is_moving_before = (WasJustMoving[x][y] != 0);
5035 #else
5036   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5037   is_moving_before = WasJustMoving[x][y];
5038 #endif
5039 #else
5040   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5041 #endif
5042   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5043
5044   /* reset animation only for moving elements which change direction of moving
5045      or which just started or stopped moving
5046      (else CEs with property "can move" / "not moving" are reset each frame) */
5047 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5048 #if 1
5049   if (is_moving_before != is_moving_after ||
5050       direction != MovDir[x][y])
5051     ResetGfxAnimation(x, y);
5052 #else
5053   if ((is_moving_before || is_moving_after) && !continues_moving)
5054     ResetGfxAnimation(x, y);
5055 #endif
5056 #else
5057   if (!continues_moving)
5058     ResetGfxAnimation(x, y);
5059 #endif
5060
5061   MovDir[x][y] = direction;
5062   GfxDir[x][y] = direction;
5063
5064 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5065   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5066                      direction == MV_DOWN && CAN_FALL(element) ?
5067                      ACTION_FALLING : ACTION_MOVING);
5068 #else
5069   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5070                      ACTION_FALLING : ACTION_MOVING);
5071 #endif
5072
5073   /* this is needed for CEs with property "can move" / "not moving" */
5074
5075   if (is_moving_after)
5076   {
5077     if (Feld[newx][newy] == EL_EMPTY)
5078       Feld[newx][newy] = EL_BLOCKED;
5079
5080     MovDir[newx][newy] = MovDir[x][y];
5081
5082 #if USE_NEW_CUSTOM_VALUE
5083     CustomValue[newx][newy] = CustomValue[x][y];
5084 #endif
5085
5086     GfxFrame[newx][newy] = GfxFrame[x][y];
5087     GfxRandom[newx][newy] = GfxRandom[x][y];
5088     GfxAction[newx][newy] = GfxAction[x][y];
5089     GfxDir[newx][newy] = GfxDir[x][y];
5090   }
5091 }
5092
5093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5094 {
5095   int direction = MovDir[x][y];
5096   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5097   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5098
5099   *goes_to_x = newx;
5100   *goes_to_y = newy;
5101 }
5102
5103 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5104 {
5105   int oldx = x, oldy = y;
5106   int direction = MovDir[x][y];
5107
5108   if (direction == MV_LEFT)
5109     oldx++;
5110   else if (direction == MV_RIGHT)
5111     oldx--;
5112   else if (direction == MV_UP)
5113     oldy++;
5114   else if (direction == MV_DOWN)
5115     oldy--;
5116
5117   *comes_from_x = oldx;
5118   *comes_from_y = oldy;
5119 }
5120
5121 int MovingOrBlocked2Element(int x, int y)
5122 {
5123   int element = Feld[x][y];
5124
5125   if (element == EL_BLOCKED)
5126   {
5127     int oldx, oldy;
5128
5129     Blocked2Moving(x, y, &oldx, &oldy);
5130     return Feld[oldx][oldy];
5131   }
5132   else
5133     return element;
5134 }
5135
5136 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5137 {
5138   /* like MovingOrBlocked2Element(), but if element is moving
5139      and (x,y) is the field the moving element is just leaving,
5140      return EL_BLOCKED instead of the element value */
5141   int element = Feld[x][y];
5142
5143   if (IS_MOVING(x, y))
5144   {
5145     if (element == EL_BLOCKED)
5146     {
5147       int oldx, oldy;
5148
5149       Blocked2Moving(x, y, &oldx, &oldy);
5150       return Feld[oldx][oldy];
5151     }
5152     else
5153       return EL_BLOCKED;
5154   }
5155   else
5156     return element;
5157 }
5158
5159 static void RemoveField(int x, int y)
5160 {
5161   Feld[x][y] = EL_EMPTY;
5162
5163   MovPos[x][y] = 0;
5164   MovDir[x][y] = 0;
5165   MovDelay[x][y] = 0;
5166
5167 #if USE_NEW_CUSTOM_VALUE
5168   CustomValue[x][y] = 0;
5169 #endif
5170
5171   AmoebaNr[x][y] = 0;
5172   ChangeDelay[x][y] = 0;
5173   ChangePage[x][y] = -1;
5174   Pushed[x][y] = FALSE;
5175
5176 #if 0
5177   ExplodeField[x][y] = EX_TYPE_NONE;
5178 #endif
5179
5180   GfxElement[x][y] = EL_UNDEFINED;
5181   GfxAction[x][y] = ACTION_DEFAULT;
5182   GfxDir[x][y] = MV_NONE;
5183 #if 0
5184   /* !!! this would prevent the removed tile from being redrawn !!! */
5185   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5186 #endif
5187 }
5188
5189 void RemoveMovingField(int x, int y)
5190 {
5191   int oldx = x, oldy = y, newx = x, newy = y;
5192   int element = Feld[x][y];
5193   int next_element = EL_UNDEFINED;
5194
5195   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5196     return;
5197
5198   if (IS_MOVING(x, y))
5199   {
5200     Moving2Blocked(x, y, &newx, &newy);
5201
5202     if (Feld[newx][newy] != EL_BLOCKED)
5203     {
5204       /* element is moving, but target field is not free (blocked), but
5205          already occupied by something different (example: acid pool);
5206          in this case, only remove the moving field, but not the target */
5207
5208       RemoveField(oldx, oldy);
5209
5210       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5211
5212       TEST_DrawLevelField(oldx, oldy);
5213
5214       return;
5215     }
5216   }
5217   else if (element == EL_BLOCKED)
5218   {
5219     Blocked2Moving(x, y, &oldx, &oldy);
5220     if (!IS_MOVING(oldx, oldy))
5221       return;
5222   }
5223
5224   if (element == EL_BLOCKED &&
5225       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5226        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5227        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5228        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5229        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5230        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5231     next_element = get_next_element(Feld[oldx][oldy]);
5232
5233   RemoveField(oldx, oldy);
5234   RemoveField(newx, newy);
5235
5236   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5237
5238   if (next_element != EL_UNDEFINED)
5239     Feld[oldx][oldy] = next_element;
5240
5241   TEST_DrawLevelField(oldx, oldy);
5242   TEST_DrawLevelField(newx, newy);
5243 }
5244
5245 void DrawDynamite(int x, int y)
5246 {
5247   int sx = SCREENX(x), sy = SCREENY(y);
5248   int graphic = el2img(Feld[x][y]);
5249   int frame;
5250
5251   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5252     return;
5253
5254   if (IS_WALKABLE_INSIDE(Back[x][y]))
5255     return;
5256
5257   if (Back[x][y])
5258     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5259   else if (Store[x][y])
5260     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5261
5262   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5263
5264   if (Back[x][y] || Store[x][y])
5265     DrawGraphicThruMask(sx, sy, graphic, frame);
5266   else
5267     DrawGraphic(sx, sy, graphic, frame);
5268 }
5269
5270 void CheckDynamite(int x, int y)
5271 {
5272   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5273   {
5274     MovDelay[x][y]--;
5275
5276     if (MovDelay[x][y] != 0)
5277     {
5278       DrawDynamite(x, y);
5279       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5280
5281       return;
5282     }
5283   }
5284
5285   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286
5287   Bang(x, y);
5288 }
5289
5290 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5291 {
5292   boolean num_checked_players = 0;
5293   int i;
5294
5295   for (i = 0; i < MAX_PLAYERS; i++)
5296   {
5297     if (stored_player[i].active)
5298     {
5299       int sx = stored_player[i].jx;
5300       int sy = stored_player[i].jy;
5301
5302       if (num_checked_players == 0)
5303       {
5304         *sx1 = *sx2 = sx;
5305         *sy1 = *sy2 = sy;
5306       }
5307       else
5308       {
5309         *sx1 = MIN(*sx1, sx);
5310         *sy1 = MIN(*sy1, sy);
5311         *sx2 = MAX(*sx2, sx);
5312         *sy2 = MAX(*sy2, sy);
5313       }
5314
5315       num_checked_players++;
5316     }
5317   }
5318 }
5319
5320 static boolean checkIfAllPlayersFitToScreen_RND()
5321 {
5322   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5323
5324   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5325
5326   return (sx2 - sx1 < SCR_FIELDX &&
5327           sy2 - sy1 < SCR_FIELDY);
5328 }
5329
5330 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5331 {
5332   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5333
5334   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5335
5336   *sx = (sx1 + sx2) / 2;
5337   *sy = (sy1 + sy2) / 2;
5338 }
5339
5340 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5341                         boolean center_screen, boolean quick_relocation)
5342 {
5343   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5344   boolean no_delay = (tape.warp_forward);
5345   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5346   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5347
5348   if (quick_relocation)
5349   {
5350     int offset = game.scroll_delay_value;
5351
5352     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5353     {
5354       if (!level.shifted_relocation || center_screen)
5355       {
5356         /* quick relocation (without scrolling), with centering of screen */
5357
5358         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5359                     x > SBX_Right + MIDPOSX ? SBX_Right :
5360                     x - MIDPOSX);
5361
5362         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5363                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5364                     y - MIDPOSY);
5365       }
5366       else
5367       {
5368         /* quick relocation (without scrolling), but do not center screen */
5369
5370         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5371                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5372                                old_x - MIDPOSX);
5373
5374         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5375                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5376                                old_y - MIDPOSY);
5377
5378         int offset_x = x + (scroll_x - center_scroll_x);
5379         int offset_y = y + (scroll_y - center_scroll_y);
5380
5381         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5382                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5383                     offset_x - MIDPOSX);
5384
5385         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5386                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5387                     offset_y - MIDPOSY);
5388       }
5389     }
5390     else
5391     {
5392       /* quick relocation (without scrolling), inside visible screen area */
5393
5394       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5395           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5396         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5397
5398       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5399           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5400         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5401
5402       /* don't scroll over playfield boundaries */
5403       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5404         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5405
5406       /* don't scroll over playfield boundaries */
5407       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5408         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5409     }
5410
5411     RedrawPlayfield(TRUE, 0,0,0,0);
5412   }
5413   else
5414   {
5415 #if 1
5416     int scroll_xx, scroll_yy;
5417
5418     if (!level.shifted_relocation || center_screen)
5419     {
5420       /* visible relocation (with scrolling), with centering of screen */
5421
5422       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5423                    x > SBX_Right + MIDPOSX ? SBX_Right :
5424                    x - MIDPOSX);
5425
5426       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5427                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5428                    y - MIDPOSY);
5429     }
5430     else
5431     {
5432       /* visible relocation (with scrolling), but do not center screen */
5433
5434       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5435                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5436                              old_x - MIDPOSX);
5437
5438       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5439                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5440                              old_y - MIDPOSY);
5441
5442       int offset_x = x + (scroll_x - center_scroll_x);
5443       int offset_y = y + (scroll_y - center_scroll_y);
5444
5445       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5446                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5447                    offset_x - MIDPOSX);
5448
5449       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5450                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5451                    offset_y - MIDPOSY);
5452     }
5453
5454 #else
5455
5456     /* visible relocation (with scrolling), with centering of screen */
5457
5458     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5459                      x > SBX_Right + MIDPOSX ? SBX_Right :
5460                      x - MIDPOSX);
5461
5462     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5463                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5464                      y - MIDPOSY);
5465 #endif
5466
5467     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5468
5469     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5470     {
5471       int dx = 0, dy = 0;
5472       int fx = FX, fy = FY;
5473
5474       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5475       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5476
5477       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5478         break;
5479
5480       scroll_x -= dx;
5481       scroll_y -= dy;
5482
5483       fx += dx * TILEX / 2;
5484       fy += dy * TILEY / 2;
5485
5486       ScrollLevel(dx, dy);
5487       DrawAllPlayers();
5488
5489       /* scroll in two steps of half tile size to make things smoother */
5490       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5491       FlushDisplay();
5492       Delay(wait_delay_value);
5493
5494       /* scroll second step to align at full tile size */
5495       BackToFront();
5496       Delay(wait_delay_value);
5497     }
5498
5499     DrawAllPlayers();
5500     BackToFront();
5501     Delay(wait_delay_value);
5502   }
5503 }
5504
5505 void RelocatePlayer(int jx, int jy, int el_player_raw)
5506 {
5507   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5508   int player_nr = GET_PLAYER_NR(el_player);
5509   struct PlayerInfo *player = &stored_player[player_nr];
5510   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5511   boolean no_delay = (tape.warp_forward);
5512   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5513   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5514   int old_jx = player->jx;
5515   int old_jy = player->jy;
5516   int old_element = Feld[old_jx][old_jy];
5517   int element = Feld[jx][jy];
5518   boolean player_relocated = (old_jx != jx || old_jy != jy);
5519
5520   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5521   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5522   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5523   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5524   int leave_side_horiz = move_dir_horiz;
5525   int leave_side_vert  = move_dir_vert;
5526   int enter_side = enter_side_horiz | enter_side_vert;
5527   int leave_side = leave_side_horiz | leave_side_vert;
5528
5529   if (player->GameOver)         /* do not reanimate dead player */
5530     return;
5531
5532   if (!player_relocated)        /* no need to relocate the player */
5533     return;
5534
5535   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5536   {
5537     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5538     DrawLevelField(jx, jy);
5539   }
5540
5541   if (player->present)
5542   {
5543     while (player->MovPos)
5544     {
5545       ScrollPlayer(player, SCROLL_GO_ON);
5546       ScrollScreen(NULL, SCROLL_GO_ON);
5547
5548       AdvanceFrameAndPlayerCounters(player->index_nr);
5549
5550       DrawPlayer(player);
5551
5552       BackToFront();
5553       Delay(wait_delay_value);
5554     }
5555
5556     DrawPlayer(player);         /* needed here only to cleanup last field */
5557     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5558
5559     player->is_moving = FALSE;
5560   }
5561
5562   if (IS_CUSTOM_ELEMENT(old_element))
5563     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5564                                CE_LEFT_BY_PLAYER,
5565                                player->index_bit, leave_side);
5566
5567   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5568                                       CE_PLAYER_LEAVES_X,
5569                                       player->index_bit, leave_side);
5570
5571   Feld[jx][jy] = el_player;
5572   InitPlayerField(jx, jy, el_player, TRUE);
5573
5574   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5575   {
5576     Feld[jx][jy] = element;
5577     InitField(jx, jy, FALSE);
5578   }
5579
5580   /* only visually relocate centered player */
5581   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5582                      FALSE, level.instant_relocation);
5583
5584   TestIfPlayerTouchesBadThing(jx, jy);
5585   TestIfPlayerTouchesCustomElement(jx, jy);
5586
5587   if (IS_CUSTOM_ELEMENT(element))
5588     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5589                                player->index_bit, enter_side);
5590
5591   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5592                                       player->index_bit, enter_side);
5593 }
5594
5595 void Explode(int ex, int ey, int phase, int mode)
5596 {
5597   int x, y;
5598   int last_phase;
5599   int border_element;
5600
5601   /* !!! eliminate this variable !!! */
5602   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5603
5604   if (game.explosions_delayed)
5605   {
5606     ExplodeField[ex][ey] = mode;
5607     return;
5608   }
5609
5610   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5611   {
5612     int center_element = Feld[ex][ey];
5613     int artwork_element, explosion_element;     /* set these values later */
5614
5615 #if 0
5616     /* --- This is only really needed (and now handled) in "Impact()". --- */
5617     /* do not explode moving elements that left the explode field in time */
5618     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5619         center_element == EL_EMPTY &&
5620         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5621       return;
5622 #endif
5623
5624 #if 0
5625     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5626     if (mode == EX_TYPE_NORMAL ||
5627         mode == EX_TYPE_CENTER ||
5628         mode == EX_TYPE_CROSS)
5629       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5630 #endif
5631
5632     /* remove things displayed in background while burning dynamite */
5633     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5634       Back[ex][ey] = 0;
5635
5636     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5637     {
5638       /* put moving element to center field (and let it explode there) */
5639       center_element = MovingOrBlocked2Element(ex, ey);
5640       RemoveMovingField(ex, ey);
5641       Feld[ex][ey] = center_element;
5642     }
5643
5644     /* now "center_element" is finally determined -- set related values now */
5645     artwork_element = center_element;           /* for custom player artwork */
5646     explosion_element = center_element;         /* for custom player artwork */
5647
5648     if (IS_PLAYER(ex, ey))
5649     {
5650       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5651
5652       artwork_element = stored_player[player_nr].artwork_element;
5653
5654       if (level.use_explosion_element[player_nr])
5655       {
5656         explosion_element = level.explosion_element[player_nr];
5657         artwork_element = explosion_element;
5658       }
5659     }
5660
5661 #if 1
5662     if (mode == EX_TYPE_NORMAL ||
5663         mode == EX_TYPE_CENTER ||
5664         mode == EX_TYPE_CROSS)
5665       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5666 #endif
5667
5668     last_phase = element_info[explosion_element].explosion_delay + 1;
5669
5670     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5671     {
5672       int xx = x - ex + 1;
5673       int yy = y - ey + 1;
5674       int element;
5675
5676       if (!IN_LEV_FIELD(x, y) ||
5677           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5678           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5679         continue;
5680
5681       element = Feld[x][y];
5682
5683       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5684       {
5685         element = MovingOrBlocked2Element(x, y);
5686
5687         if (!IS_EXPLOSION_PROOF(element))
5688           RemoveMovingField(x, y);
5689       }
5690
5691       /* indestructible elements can only explode in center (but not flames) */
5692       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5693                                            mode == EX_TYPE_BORDER)) ||
5694           element == EL_FLAMES)
5695         continue;
5696
5697       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5698          behaviour, for example when touching a yamyam that explodes to rocks
5699          with active deadly shield, a rock is created under the player !!! */
5700       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5701 #if 0
5702       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5703           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5704            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5705 #else
5706       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5707 #endif
5708       {
5709         if (IS_ACTIVE_BOMB(element))
5710         {
5711           /* re-activate things under the bomb like gate or penguin */
5712           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5713           Back[x][y] = 0;
5714         }
5715
5716         continue;
5717       }
5718
5719       /* save walkable background elements while explosion on same tile */
5720       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5721           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5722         Back[x][y] = element;
5723
5724       /* ignite explodable elements reached by other explosion */
5725       if (element == EL_EXPLOSION)
5726         element = Store2[x][y];
5727
5728       if (AmoebaNr[x][y] &&
5729           (element == EL_AMOEBA_FULL ||
5730            element == EL_BD_AMOEBA ||
5731            element == EL_AMOEBA_GROWING))
5732       {
5733         AmoebaCnt[AmoebaNr[x][y]]--;
5734         AmoebaCnt2[AmoebaNr[x][y]]--;
5735       }
5736
5737       RemoveField(x, y);
5738
5739       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5740       {
5741         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5742
5743         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5744
5745         if (PLAYERINFO(ex, ey)->use_murphy)
5746           Store[x][y] = EL_EMPTY;
5747       }
5748
5749       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5750          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5751       else if (ELEM_IS_PLAYER(center_element))
5752         Store[x][y] = EL_EMPTY;
5753       else if (center_element == EL_YAMYAM)
5754         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5755       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5756         Store[x][y] = element_info[center_element].content.e[xx][yy];
5757 #if 1
5758       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5759          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5760          otherwise) -- FIX THIS !!! */
5761       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5762         Store[x][y] = element_info[element].content.e[1][1];
5763 #else
5764       else if (!CAN_EXPLODE(element))
5765         Store[x][y] = element_info[element].content.e[1][1];
5766 #endif
5767       else
5768         Store[x][y] = EL_EMPTY;
5769
5770       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5771           center_element == EL_AMOEBA_TO_DIAMOND)
5772         Store2[x][y] = element;
5773
5774       Feld[x][y] = EL_EXPLOSION;
5775       GfxElement[x][y] = artwork_element;
5776
5777       ExplodePhase[x][y] = 1;
5778       ExplodeDelay[x][y] = last_phase;
5779
5780       Stop[x][y] = TRUE;
5781     }
5782
5783     if (center_element == EL_YAMYAM)
5784       game.yamyam_content_nr =
5785         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5786
5787     return;
5788   }
5789
5790   if (Stop[ex][ey])
5791     return;
5792
5793   x = ex;
5794   y = ey;
5795
5796   if (phase == 1)
5797     GfxFrame[x][y] = 0;         /* restart explosion animation */
5798
5799   last_phase = ExplodeDelay[x][y];
5800
5801   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5802
5803 #ifdef DEBUG
5804
5805   /* activate this even in non-DEBUG version until cause for crash in
5806      getGraphicAnimationFrame() (see below) is found and eliminated */
5807
5808 #endif
5809 #if 1
5810
5811 #if 1
5812   /* this can happen if the player leaves an explosion just in time */
5813   if (GfxElement[x][y] == EL_UNDEFINED)
5814     GfxElement[x][y] = EL_EMPTY;
5815 #else
5816   if (GfxElement[x][y] == EL_UNDEFINED)
5817   {
5818     printf("\n\n");
5819     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5820     printf("Explode(): This should never happen!\n");
5821     printf("\n\n");
5822
5823     GfxElement[x][y] = EL_EMPTY;
5824   }
5825 #endif
5826
5827 #endif
5828
5829   border_element = Store2[x][y];
5830   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5831     border_element = StorePlayer[x][y];
5832
5833   if (phase == element_info[border_element].ignition_delay ||
5834       phase == last_phase)
5835   {
5836     boolean border_explosion = FALSE;
5837
5838     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5839         !PLAYER_EXPLOSION_PROTECTED(x, y))
5840     {
5841       KillPlayerUnlessExplosionProtected(x, y);
5842       border_explosion = TRUE;
5843     }
5844     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5845     {
5846       Feld[x][y] = Store2[x][y];
5847       Store2[x][y] = 0;
5848       Bang(x, y);
5849       border_explosion = TRUE;
5850     }
5851     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5852     {
5853       AmoebeUmwandeln(x, y);
5854       Store2[x][y] = 0;
5855       border_explosion = TRUE;
5856     }
5857
5858     /* if an element just explodes due to another explosion (chain-reaction),
5859        do not immediately end the new explosion when it was the last frame of
5860        the explosion (as it would be done in the following "if"-statement!) */
5861     if (border_explosion && phase == last_phase)
5862       return;
5863   }
5864
5865   if (phase == last_phase)
5866   {
5867     int element;
5868
5869     element = Feld[x][y] = Store[x][y];
5870     Store[x][y] = Store2[x][y] = 0;
5871     GfxElement[x][y] = EL_UNDEFINED;
5872
5873     /* player can escape from explosions and might therefore be still alive */
5874     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5875         element <= EL_PLAYER_IS_EXPLODING_4)
5876     {
5877       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5878       int explosion_element = EL_PLAYER_1 + player_nr;
5879       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5880       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5881
5882       if (level.use_explosion_element[player_nr])
5883         explosion_element = level.explosion_element[player_nr];
5884
5885       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5886                     element_info[explosion_element].content.e[xx][yy]);
5887     }
5888
5889     /* restore probably existing indestructible background element */
5890     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5891       element = Feld[x][y] = Back[x][y];
5892     Back[x][y] = 0;
5893
5894     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5895     GfxDir[x][y] = MV_NONE;
5896     ChangeDelay[x][y] = 0;
5897     ChangePage[x][y] = -1;
5898
5899 #if USE_NEW_CUSTOM_VALUE
5900     CustomValue[x][y] = 0;
5901 #endif
5902
5903     InitField_WithBug2(x, y, FALSE);
5904
5905     TEST_DrawLevelField(x, y);
5906
5907     TestIfElementTouchesCustomElement(x, y);
5908
5909     if (GFX_CRUMBLED(element))
5910       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5911
5912     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5913       StorePlayer[x][y] = 0;
5914
5915     if (ELEM_IS_PLAYER(element))
5916       RelocatePlayer(x, y, element);
5917   }
5918   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5919   {
5920     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5921     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5922
5923     if (phase == delay)
5924       TEST_DrawLevelFieldCrumbledSand(x, y);
5925
5926     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5927     {
5928       DrawLevelElement(x, y, Back[x][y]);
5929       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5930     }
5931     else if (IS_WALKABLE_UNDER(Back[x][y]))
5932     {
5933       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934       DrawLevelElementThruMask(x, y, Back[x][y]);
5935     }
5936     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5937       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5938   }
5939 }
5940
5941 void DynaExplode(int ex, int ey)
5942 {
5943   int i, j;
5944   int dynabomb_element = Feld[ex][ey];
5945   int dynabomb_size = 1;
5946   boolean dynabomb_xl = FALSE;
5947   struct PlayerInfo *player;
5948   static int xy[4][2] =
5949   {
5950     { 0, -1 },
5951     { -1, 0 },
5952     { +1, 0 },
5953     { 0, +1 }
5954   };
5955
5956   if (IS_ACTIVE_BOMB(dynabomb_element))
5957   {
5958     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5959     dynabomb_size = player->dynabomb_size;
5960     dynabomb_xl = player->dynabomb_xl;
5961     player->dynabombs_left++;
5962   }
5963
5964   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5965
5966   for (i = 0; i < NUM_DIRECTIONS; i++)
5967   {
5968     for (j = 1; j <= dynabomb_size; j++)
5969     {
5970       int x = ex + j * xy[i][0];
5971       int y = ey + j * xy[i][1];
5972       int element;
5973
5974       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5975         break;
5976
5977       element = Feld[x][y];
5978
5979       /* do not restart explosions of fields with active bombs */
5980       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5981         continue;
5982
5983       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5984
5985       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5986           !IS_DIGGABLE(element) && !dynabomb_xl)
5987         break;
5988     }
5989   }
5990 }
5991
5992 void Bang(int x, int y)
5993 {
5994   int element = MovingOrBlocked2Element(x, y);
5995   int explosion_type = EX_TYPE_NORMAL;
5996
5997   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5998   {
5999     struct PlayerInfo *player = PLAYERINFO(x, y);
6000
6001 #if USE_FIX_CE_ACTION_WITH_PLAYER
6002     element = Feld[x][y] = player->initial_element;
6003 #else
6004     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6005                             player->element_nr);
6006 #endif
6007
6008     if (level.use_explosion_element[player->index_nr])
6009     {
6010       int explosion_element = level.explosion_element[player->index_nr];
6011
6012       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6013         explosion_type = EX_TYPE_CROSS;
6014       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6015         explosion_type = EX_TYPE_CENTER;
6016     }
6017   }
6018
6019   switch (element)
6020   {
6021     case EL_BUG:
6022     case EL_SPACESHIP:
6023     case EL_BD_BUTTERFLY:
6024     case EL_BD_FIREFLY:
6025     case EL_YAMYAM:
6026     case EL_DARK_YAMYAM:
6027     case EL_ROBOT:
6028     case EL_PACMAN:
6029     case EL_MOLE:
6030       RaiseScoreElement(element);
6031       break;
6032
6033     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6034     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6035     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6036     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6037     case EL_DYNABOMB_INCREASE_NUMBER:
6038     case EL_DYNABOMB_INCREASE_SIZE:
6039     case EL_DYNABOMB_INCREASE_POWER:
6040       explosion_type = EX_TYPE_DYNA;
6041       break;
6042
6043     case EL_DC_LANDMINE:
6044 #if 0
6045     case EL_EM_EXIT_OPEN:
6046     case EL_EM_STEEL_EXIT_OPEN:
6047 #endif
6048       explosion_type = EX_TYPE_CENTER;
6049       break;
6050
6051     case EL_PENGUIN:
6052     case EL_LAMP:
6053     case EL_LAMP_ACTIVE:
6054     case EL_AMOEBA_TO_DIAMOND:
6055       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6056         explosion_type = EX_TYPE_CENTER;
6057       break;
6058
6059     default:
6060       if (element_info[element].explosion_type == EXPLODES_CROSS)
6061         explosion_type = EX_TYPE_CROSS;
6062       else if (element_info[element].explosion_type == EXPLODES_1X1)
6063         explosion_type = EX_TYPE_CENTER;
6064       break;
6065   }
6066
6067   if (explosion_type == EX_TYPE_DYNA)
6068     DynaExplode(x, y);
6069   else
6070     Explode(x, y, EX_PHASE_START, explosion_type);
6071
6072   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6073 }
6074
6075 void SplashAcid(int x, int y)
6076 {
6077   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6078       (!IN_LEV_FIELD(x - 1, y - 2) ||
6079        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6080     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6081
6082   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6083       (!IN_LEV_FIELD(x + 1, y - 2) ||
6084        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6085     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6086
6087   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6088 }
6089
6090 static void InitBeltMovement()
6091 {
6092   static int belt_base_element[4] =
6093   {
6094     EL_CONVEYOR_BELT_1_LEFT,
6095     EL_CONVEYOR_BELT_2_LEFT,
6096     EL_CONVEYOR_BELT_3_LEFT,
6097     EL_CONVEYOR_BELT_4_LEFT
6098   };
6099   static int belt_base_active_element[4] =
6100   {
6101     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6102     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6103     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6104     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6105   };
6106
6107   int x, y, i, j;
6108
6109   /* set frame order for belt animation graphic according to belt direction */
6110   for (i = 0; i < NUM_BELTS; i++)
6111   {
6112     int belt_nr = i;
6113
6114     for (j = 0; j < NUM_BELT_PARTS; j++)
6115     {
6116       int element = belt_base_active_element[belt_nr] + j;
6117       int graphic_1 = el2img(element);
6118       int graphic_2 = el2panelimg(element);
6119
6120       if (game.belt_dir[i] == MV_LEFT)
6121       {
6122         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6123         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6124       }
6125       else
6126       {
6127         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6128         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6129       }
6130     }
6131   }
6132
6133   SCAN_PLAYFIELD(x, y)
6134   {
6135     int element = Feld[x][y];
6136
6137     for (i = 0; i < NUM_BELTS; i++)
6138     {
6139       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6140       {
6141         int e_belt_nr = getBeltNrFromBeltElement(element);
6142         int belt_nr = i;
6143
6144         if (e_belt_nr == belt_nr)
6145         {
6146           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6147
6148           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6149         }
6150       }
6151     }
6152   }
6153 }
6154
6155 static void ToggleBeltSwitch(int x, int y)
6156 {
6157   static int belt_base_element[4] =
6158   {
6159     EL_CONVEYOR_BELT_1_LEFT,
6160     EL_CONVEYOR_BELT_2_LEFT,
6161     EL_CONVEYOR_BELT_3_LEFT,
6162     EL_CONVEYOR_BELT_4_LEFT
6163   };
6164   static int belt_base_active_element[4] =
6165   {
6166     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6167     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6170   };
6171   static int belt_base_switch_element[4] =
6172   {
6173     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6174     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6175     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6176     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6177   };
6178   static int belt_move_dir[4] =
6179   {
6180     MV_LEFT,
6181     MV_NONE,
6182     MV_RIGHT,
6183     MV_NONE,
6184   };
6185
6186   int element = Feld[x][y];
6187   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6188   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6189   int belt_dir = belt_move_dir[belt_dir_nr];
6190   int xx, yy, i;
6191
6192   if (!IS_BELT_SWITCH(element))
6193     return;
6194
6195   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6196   game.belt_dir[belt_nr] = belt_dir;
6197
6198   if (belt_dir_nr == 3)
6199     belt_dir_nr = 1;
6200
6201   /* set frame order for belt animation graphic according to belt direction */
6202   for (i = 0; i < NUM_BELT_PARTS; i++)
6203   {
6204     int element = belt_base_active_element[belt_nr] + i;
6205     int graphic_1 = el2img(element);
6206     int graphic_2 = el2panelimg(element);
6207
6208     if (belt_dir == MV_LEFT)
6209     {
6210       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6211       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6212     }
6213     else
6214     {
6215       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6216       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6217     }
6218   }
6219
6220   SCAN_PLAYFIELD(xx, yy)
6221   {
6222     int element = Feld[xx][yy];
6223
6224     if (IS_BELT_SWITCH(element))
6225     {
6226       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6227
6228       if (e_belt_nr == belt_nr)
6229       {
6230         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6231         TEST_DrawLevelField(xx, yy);
6232       }
6233     }
6234     else if (IS_BELT(element) && belt_dir != MV_NONE)
6235     {
6236       int e_belt_nr = getBeltNrFromBeltElement(element);
6237
6238       if (e_belt_nr == belt_nr)
6239       {
6240         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6241
6242         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6243         TEST_DrawLevelField(xx, yy);
6244       }
6245     }
6246     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6247     {
6248       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6249
6250       if (e_belt_nr == belt_nr)
6251       {
6252         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6253
6254         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6255         TEST_DrawLevelField(xx, yy);
6256       }
6257     }
6258   }
6259 }
6260
6261 static void ToggleSwitchgateSwitch(int x, int y)
6262 {
6263   int xx, yy;
6264
6265   game.switchgate_pos = !game.switchgate_pos;
6266
6267   SCAN_PLAYFIELD(xx, yy)
6268   {
6269     int element = Feld[xx][yy];
6270
6271 #if !USE_BOTH_SWITCHGATE_SWITCHES
6272     if (element == EL_SWITCHGATE_SWITCH_UP ||
6273         element == EL_SWITCHGATE_SWITCH_DOWN)
6274     {
6275       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6276       TEST_DrawLevelField(xx, yy);
6277     }
6278     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6279              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6280     {
6281       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6282       TEST_DrawLevelField(xx, yy);
6283     }
6284 #else
6285     if (element == EL_SWITCHGATE_SWITCH_UP)
6286     {
6287       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6288       TEST_DrawLevelField(xx, yy);
6289     }
6290     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6291     {
6292       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6293       TEST_DrawLevelField(xx, yy);
6294     }
6295     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6296     {
6297       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6298       TEST_DrawLevelField(xx, yy);
6299     }
6300     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6301     {
6302       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6303       TEST_DrawLevelField(xx, yy);
6304     }
6305 #endif
6306     else if (element == EL_SWITCHGATE_OPEN ||
6307              element == EL_SWITCHGATE_OPENING)
6308     {
6309       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6310
6311       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6312     }
6313     else if (element == EL_SWITCHGATE_CLOSED ||
6314              element == EL_SWITCHGATE_CLOSING)
6315     {
6316       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6317
6318       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6319     }
6320   }
6321 }
6322
6323 static int getInvisibleActiveFromInvisibleElement(int element)
6324 {
6325   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6326           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6327           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6328           element);
6329 }
6330
6331 static int getInvisibleFromInvisibleActiveElement(int element)
6332 {
6333   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6334           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6335           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6336           element);
6337 }
6338
6339 static void RedrawAllLightSwitchesAndInvisibleElements()
6340 {
6341   int x, y;
6342
6343   SCAN_PLAYFIELD(x, y)
6344   {
6345     int element = Feld[x][y];
6346
6347     if (element == EL_LIGHT_SWITCH &&
6348         game.light_time_left > 0)
6349     {
6350       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6351       TEST_DrawLevelField(x, y);
6352     }
6353     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6354              game.light_time_left == 0)
6355     {
6356       Feld[x][y] = EL_LIGHT_SWITCH;
6357       TEST_DrawLevelField(x, y);
6358     }
6359     else if (element == EL_EMC_DRIPPER &&
6360              game.light_time_left > 0)
6361     {
6362       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6363       TEST_DrawLevelField(x, y);
6364     }
6365     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6366              game.light_time_left == 0)
6367     {
6368       Feld[x][y] = EL_EMC_DRIPPER;
6369       TEST_DrawLevelField(x, y);
6370     }
6371     else if (element == EL_INVISIBLE_STEELWALL ||
6372              element == EL_INVISIBLE_WALL ||
6373              element == EL_INVISIBLE_SAND)
6374     {
6375       if (game.light_time_left > 0)
6376         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6377
6378       TEST_DrawLevelField(x, y);
6379
6380       /* uncrumble neighbour fields, if needed */
6381       if (element == EL_INVISIBLE_SAND)
6382         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6383     }
6384     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6385              element == EL_INVISIBLE_WALL_ACTIVE ||
6386              element == EL_INVISIBLE_SAND_ACTIVE)
6387     {
6388       if (game.light_time_left == 0)
6389         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6390
6391       TEST_DrawLevelField(x, y);
6392
6393       /* re-crumble neighbour fields, if needed */
6394       if (element == EL_INVISIBLE_SAND)
6395         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6396     }
6397   }
6398 }
6399
6400 static void RedrawAllInvisibleElementsForLenses()
6401 {
6402   int x, y;
6403
6404   SCAN_PLAYFIELD(x, y)
6405   {
6406     int element = Feld[x][y];
6407
6408     if (element == EL_EMC_DRIPPER &&
6409         game.lenses_time_left > 0)
6410     {
6411       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6412       TEST_DrawLevelField(x, y);
6413     }
6414     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6415              game.lenses_time_left == 0)
6416     {
6417       Feld[x][y] = EL_EMC_DRIPPER;
6418       TEST_DrawLevelField(x, y);
6419     }
6420     else if (element == EL_INVISIBLE_STEELWALL ||
6421              element == EL_INVISIBLE_WALL ||
6422              element == EL_INVISIBLE_SAND)
6423     {
6424       if (game.lenses_time_left > 0)
6425         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6426
6427       TEST_DrawLevelField(x, y);
6428
6429       /* uncrumble neighbour fields, if needed */
6430       if (element == EL_INVISIBLE_SAND)
6431         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6432     }
6433     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6434              element == EL_INVISIBLE_WALL_ACTIVE ||
6435              element == EL_INVISIBLE_SAND_ACTIVE)
6436     {
6437       if (game.lenses_time_left == 0)
6438         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6439
6440       TEST_DrawLevelField(x, y);
6441
6442       /* re-crumble neighbour fields, if needed */
6443       if (element == EL_INVISIBLE_SAND)
6444         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6445     }
6446   }
6447 }
6448
6449 static void RedrawAllInvisibleElementsForMagnifier()
6450 {
6451   int x, y;
6452
6453   SCAN_PLAYFIELD(x, y)
6454   {
6455     int element = Feld[x][y];
6456
6457     if (element == EL_EMC_FAKE_GRASS &&
6458         game.magnify_time_left > 0)
6459     {
6460       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6461       TEST_DrawLevelField(x, y);
6462     }
6463     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6464              game.magnify_time_left == 0)
6465     {
6466       Feld[x][y] = EL_EMC_FAKE_GRASS;
6467       TEST_DrawLevelField(x, y);
6468     }
6469     else if (IS_GATE_GRAY(element) &&
6470              game.magnify_time_left > 0)
6471     {
6472       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6473                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6474                     IS_EM_GATE_GRAY(element) ?
6475                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6476                     IS_EMC_GATE_GRAY(element) ?
6477                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6478                     IS_DC_GATE_GRAY(element) ?
6479                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6480                     element);
6481       TEST_DrawLevelField(x, y);
6482     }
6483     else if (IS_GATE_GRAY_ACTIVE(element) &&
6484              game.magnify_time_left == 0)
6485     {
6486       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6487                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6488                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6489                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6490                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6491                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6492                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6493                     EL_DC_GATE_WHITE_GRAY :
6494                     element);
6495       TEST_DrawLevelField(x, y);
6496     }
6497   }
6498 }
6499
6500 static void ToggleLightSwitch(int x, int y)
6501 {
6502   int element = Feld[x][y];
6503
6504   game.light_time_left =
6505     (element == EL_LIGHT_SWITCH ?
6506      level.time_light * FRAMES_PER_SECOND : 0);
6507
6508   RedrawAllLightSwitchesAndInvisibleElements();
6509 }
6510
6511 static void ActivateTimegateSwitch(int x, int y)
6512 {
6513   int xx, yy;
6514
6515   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6516
6517   SCAN_PLAYFIELD(xx, yy)
6518   {
6519     int element = Feld[xx][yy];
6520
6521     if (element == EL_TIMEGATE_CLOSED ||
6522         element == EL_TIMEGATE_CLOSING)
6523     {
6524       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6525       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6526     }
6527
6528     /*
6529     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6530     {
6531       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6532       TEST_DrawLevelField(xx, yy);
6533     }
6534     */
6535
6536   }
6537
6538 #if 1
6539   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6540                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6541 #else
6542   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6543 #endif
6544 }
6545
6546 void Impact(int x, int y)
6547 {
6548   boolean last_line = (y == lev_fieldy - 1);
6549   boolean object_hit = FALSE;
6550   boolean impact = (last_line || object_hit);
6551   int element = Feld[x][y];
6552   int smashed = EL_STEELWALL;
6553
6554   if (!last_line)       /* check if element below was hit */
6555   {
6556     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6557       return;
6558
6559     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6560                                          MovDir[x][y + 1] != MV_DOWN ||
6561                                          MovPos[x][y + 1] <= TILEY / 2));
6562
6563     /* do not smash moving elements that left the smashed field in time */
6564     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6565         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6566       object_hit = FALSE;
6567
6568 #if USE_QUICKSAND_IMPACT_BUGFIX
6569     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6570     {
6571       RemoveMovingField(x, y + 1);
6572       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6573       Feld[x][y + 2] = EL_ROCK;
6574       TEST_DrawLevelField(x, y + 2);
6575
6576       object_hit = TRUE;
6577     }
6578
6579     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6580     {
6581       RemoveMovingField(x, y + 1);
6582       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6583       Feld[x][y + 2] = EL_ROCK;
6584       TEST_DrawLevelField(x, y + 2);
6585
6586       object_hit = TRUE;
6587     }
6588 #endif
6589
6590     if (object_hit)
6591       smashed = MovingOrBlocked2Element(x, y + 1);
6592
6593     impact = (last_line || object_hit);
6594   }
6595
6596   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6597   {
6598     SplashAcid(x, y + 1);
6599     return;
6600   }
6601
6602   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6603   /* only reset graphic animation if graphic really changes after impact */
6604   if (impact &&
6605       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6606   {
6607     ResetGfxAnimation(x, y);
6608     TEST_DrawLevelField(x, y);
6609   }
6610
6611   if (impact && CAN_EXPLODE_IMPACT(element))
6612   {
6613     Bang(x, y);
6614     return;
6615   }
6616   else if (impact && element == EL_PEARL &&
6617            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6618   {
6619     ResetGfxAnimation(x, y);
6620
6621     Feld[x][y] = EL_PEARL_BREAKING;
6622     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6623     return;
6624   }
6625   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6626   {
6627     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6628
6629     return;
6630   }
6631
6632   if (impact && element == EL_AMOEBA_DROP)
6633   {
6634     if (object_hit && IS_PLAYER(x, y + 1))
6635       KillPlayerUnlessEnemyProtected(x, y + 1);
6636     else if (object_hit && smashed == EL_PENGUIN)
6637       Bang(x, y + 1);
6638     else
6639     {
6640       Feld[x][y] = EL_AMOEBA_GROWING;
6641       Store[x][y] = EL_AMOEBA_WET;
6642
6643       ResetRandomAnimationValue(x, y);
6644     }
6645     return;
6646   }
6647
6648   if (object_hit)               /* check which object was hit */
6649   {
6650     if ((CAN_PASS_MAGIC_WALL(element) && 
6651          (smashed == EL_MAGIC_WALL ||
6652           smashed == EL_BD_MAGIC_WALL)) ||
6653         (CAN_PASS_DC_MAGIC_WALL(element) &&
6654          smashed == EL_DC_MAGIC_WALL))
6655     {
6656       int xx, yy;
6657       int activated_magic_wall =
6658         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6659          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6660          EL_DC_MAGIC_WALL_ACTIVE);
6661
6662       /* activate magic wall / mill */
6663       SCAN_PLAYFIELD(xx, yy)
6664       {
6665         if (Feld[xx][yy] == smashed)
6666           Feld[xx][yy] = activated_magic_wall;
6667       }
6668
6669       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6670       game.magic_wall_active = TRUE;
6671
6672       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6673                             SND_MAGIC_WALL_ACTIVATING :
6674                             smashed == EL_BD_MAGIC_WALL ?
6675                             SND_BD_MAGIC_WALL_ACTIVATING :
6676                             SND_DC_MAGIC_WALL_ACTIVATING));
6677     }
6678
6679     if (IS_PLAYER(x, y + 1))
6680     {
6681       if (CAN_SMASH_PLAYER(element))
6682       {
6683         KillPlayerUnlessEnemyProtected(x, y + 1);
6684         return;
6685       }
6686     }
6687     else if (smashed == EL_PENGUIN)
6688     {
6689       if (CAN_SMASH_PLAYER(element))
6690       {
6691         Bang(x, y + 1);
6692         return;
6693       }
6694     }
6695     else if (element == EL_BD_DIAMOND)
6696     {
6697       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6698       {
6699         Bang(x, y + 1);
6700         return;
6701       }
6702     }
6703     else if (((element == EL_SP_INFOTRON ||
6704                element == EL_SP_ZONK) &&
6705               (smashed == EL_SP_SNIKSNAK ||
6706                smashed == EL_SP_ELECTRON ||
6707                smashed == EL_SP_DISK_ORANGE)) ||
6708              (element == EL_SP_INFOTRON &&
6709               smashed == EL_SP_DISK_YELLOW))
6710     {
6711       Bang(x, y + 1);
6712       return;
6713     }
6714     else if (CAN_SMASH_EVERYTHING(element))
6715     {
6716       if (IS_CLASSIC_ENEMY(smashed) ||
6717           CAN_EXPLODE_SMASHED(smashed))
6718       {
6719         Bang(x, y + 1);
6720         return;
6721       }
6722       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6723       {
6724         if (smashed == EL_LAMP ||
6725             smashed == EL_LAMP_ACTIVE)
6726         {
6727           Bang(x, y + 1);
6728           return;
6729         }
6730         else if (smashed == EL_NUT)
6731         {
6732           Feld[x][y + 1] = EL_NUT_BREAKING;
6733           PlayLevelSound(x, y, SND_NUT_BREAKING);
6734           RaiseScoreElement(EL_NUT);
6735           return;
6736         }
6737         else if (smashed == EL_PEARL)
6738         {
6739           ResetGfxAnimation(x, y);
6740
6741           Feld[x][y + 1] = EL_PEARL_BREAKING;
6742           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6743           return;
6744         }
6745         else if (smashed == EL_DIAMOND)
6746         {
6747           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6748           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6749           return;
6750         }
6751         else if (IS_BELT_SWITCH(smashed))
6752         {
6753           ToggleBeltSwitch(x, y + 1);
6754         }
6755         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6756                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6757                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6758                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6759         {
6760           ToggleSwitchgateSwitch(x, y + 1);
6761         }
6762         else if (smashed == EL_LIGHT_SWITCH ||
6763                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6764         {
6765           ToggleLightSwitch(x, y + 1);
6766         }
6767         else
6768         {
6769 #if 0
6770           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6771 #endif
6772
6773           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6774
6775           CheckElementChangeBySide(x, y + 1, smashed, element,
6776                                    CE_SWITCHED, CH_SIDE_TOP);
6777           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6778                                             CH_SIDE_TOP);
6779         }
6780       }
6781       else
6782       {
6783         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6784       }
6785     }
6786   }
6787
6788   /* play sound of magic wall / mill */
6789   if (!last_line &&
6790       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6791        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6792        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6793   {
6794     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6795       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6796     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6797       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6798     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6799       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6800
6801     return;
6802   }
6803
6804   /* play sound of object that hits the ground */
6805   if (last_line || object_hit)
6806     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6807 }
6808
6809 inline static void TurnRoundExt(int x, int y)
6810 {
6811   static struct
6812   {
6813     int dx, dy;
6814   } move_xy[] =
6815   {
6816     {  0,  0 },
6817     { -1,  0 },
6818     { +1,  0 },
6819     {  0,  0 },
6820     {  0, -1 },
6821     {  0,  0 }, { 0, 0 }, { 0, 0 },
6822     {  0, +1 }
6823   };
6824   static struct
6825   {
6826     int left, right, back;
6827   } turn[] =
6828   {
6829     { 0,        0,              0        },
6830     { MV_DOWN,  MV_UP,          MV_RIGHT },
6831     { MV_UP,    MV_DOWN,        MV_LEFT  },
6832     { 0,        0,              0        },
6833     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6834     { 0,        0,              0        },
6835     { 0,        0,              0        },
6836     { 0,        0,              0        },
6837     { MV_RIGHT, MV_LEFT,        MV_UP    }
6838   };
6839
6840   int element = Feld[x][y];
6841   int move_pattern = element_info[element].move_pattern;
6842
6843   int old_move_dir = MovDir[x][y];
6844   int left_dir  = turn[old_move_dir].left;
6845   int right_dir = turn[old_move_dir].right;
6846   int back_dir  = turn[old_move_dir].back;
6847
6848   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6849   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6850   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6851   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6852
6853   int left_x  = x + left_dx,  left_y  = y + left_dy;
6854   int right_x = x + right_dx, right_y = y + right_dy;
6855   int move_x  = x + move_dx,  move_y  = y + move_dy;
6856
6857   int xx, yy;
6858
6859   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6860   {
6861     TestIfBadThingTouchesOtherBadThing(x, y);
6862
6863     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6864       MovDir[x][y] = right_dir;
6865     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6866       MovDir[x][y] = left_dir;
6867
6868     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6869       MovDelay[x][y] = 9;
6870     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6871       MovDelay[x][y] = 1;
6872   }
6873   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6874   {
6875     TestIfBadThingTouchesOtherBadThing(x, y);
6876
6877     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6878       MovDir[x][y] = left_dir;
6879     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6880       MovDir[x][y] = right_dir;
6881
6882     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6883       MovDelay[x][y] = 9;
6884     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6885       MovDelay[x][y] = 1;
6886   }
6887   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6888   {
6889     TestIfBadThingTouchesOtherBadThing(x, y);
6890
6891     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6892       MovDir[x][y] = left_dir;
6893     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6894       MovDir[x][y] = right_dir;
6895
6896     if (MovDir[x][y] != old_move_dir)
6897       MovDelay[x][y] = 9;
6898   }
6899   else if (element == EL_YAMYAM)
6900   {
6901     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6902     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6903
6904     if (can_turn_left && can_turn_right)
6905       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6906     else if (can_turn_left)
6907       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6908     else if (can_turn_right)
6909       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6910     else
6911       MovDir[x][y] = back_dir;
6912
6913     MovDelay[x][y] = 16 + 16 * RND(3);
6914   }
6915   else if (element == EL_DARK_YAMYAM)
6916   {
6917     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6918                                                          left_x, left_y);
6919     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6920                                                          right_x, right_y);
6921
6922     if (can_turn_left && can_turn_right)
6923       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6924     else if (can_turn_left)
6925       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6926     else if (can_turn_right)
6927       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6928     else
6929       MovDir[x][y] = back_dir;
6930
6931     MovDelay[x][y] = 16 + 16 * RND(3);
6932   }
6933   else if (element == EL_PACMAN)
6934   {
6935     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6936     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6937
6938     if (can_turn_left && can_turn_right)
6939       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6940     else if (can_turn_left)
6941       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6942     else if (can_turn_right)
6943       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6944     else
6945       MovDir[x][y] = back_dir;
6946
6947     MovDelay[x][y] = 6 + RND(40);
6948   }
6949   else if (element == EL_PIG)
6950   {
6951     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6952     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6953     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6954     boolean should_turn_left, should_turn_right, should_move_on;
6955     int rnd_value = 24;
6956     int rnd = RND(rnd_value);
6957
6958     should_turn_left = (can_turn_left &&
6959                         (!can_move_on ||
6960                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6961                                                    y + back_dy + left_dy)));
6962     should_turn_right = (can_turn_right &&
6963                          (!can_move_on ||
6964                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6965                                                     y + back_dy + right_dy)));
6966     should_move_on = (can_move_on &&
6967                       (!can_turn_left ||
6968                        !can_turn_right ||
6969                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6970                                                  y + move_dy + left_dy) ||
6971                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6972                                                  y + move_dy + right_dy)));
6973
6974     if (should_turn_left || should_turn_right || should_move_on)
6975     {
6976       if (should_turn_left && should_turn_right && should_move_on)
6977         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6978                         rnd < 2 * rnd_value / 3 ? right_dir :
6979                         old_move_dir);
6980       else if (should_turn_left && should_turn_right)
6981         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6982       else if (should_turn_left && should_move_on)
6983         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6984       else if (should_turn_right && should_move_on)
6985         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6986       else if (should_turn_left)
6987         MovDir[x][y] = left_dir;
6988       else if (should_turn_right)
6989         MovDir[x][y] = right_dir;
6990       else if (should_move_on)
6991         MovDir[x][y] = old_move_dir;
6992     }
6993     else if (can_move_on && rnd > rnd_value / 8)
6994       MovDir[x][y] = old_move_dir;
6995     else if (can_turn_left && can_turn_right)
6996       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6997     else if (can_turn_left && rnd > rnd_value / 8)
6998       MovDir[x][y] = left_dir;
6999     else if (can_turn_right && rnd > rnd_value/8)
7000       MovDir[x][y] = right_dir;
7001     else
7002       MovDir[x][y] = back_dir;
7003
7004     xx = x + move_xy[MovDir[x][y]].dx;
7005     yy = y + move_xy[MovDir[x][y]].dy;
7006
7007     if (!IN_LEV_FIELD(xx, yy) ||
7008         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7009       MovDir[x][y] = old_move_dir;
7010
7011     MovDelay[x][y] = 0;
7012   }
7013   else if (element == EL_DRAGON)
7014   {
7015     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7016     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7017     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7018     int rnd_value = 24;
7019     int rnd = RND(rnd_value);
7020
7021     if (can_move_on && rnd > rnd_value / 8)
7022       MovDir[x][y] = old_move_dir;
7023     else if (can_turn_left && can_turn_right)
7024       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7025     else if (can_turn_left && rnd > rnd_value / 8)
7026       MovDir[x][y] = left_dir;
7027     else if (can_turn_right && rnd > rnd_value / 8)
7028       MovDir[x][y] = right_dir;
7029     else
7030       MovDir[x][y] = back_dir;
7031
7032     xx = x + move_xy[MovDir[x][y]].dx;
7033     yy = y + move_xy[MovDir[x][y]].dy;
7034
7035     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7036       MovDir[x][y] = old_move_dir;
7037
7038     MovDelay[x][y] = 0;
7039   }
7040   else if (element == EL_MOLE)
7041   {
7042     boolean can_move_on =
7043       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7044                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7045                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7046     if (!can_move_on)
7047     {
7048       boolean can_turn_left =
7049         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7050                               IS_AMOEBOID(Feld[left_x][left_y])));
7051
7052       boolean can_turn_right =
7053         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7054                               IS_AMOEBOID(Feld[right_x][right_y])));
7055
7056       if (can_turn_left && can_turn_right)
7057         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7058       else if (can_turn_left)
7059         MovDir[x][y] = left_dir;
7060       else
7061         MovDir[x][y] = right_dir;
7062     }
7063
7064     if (MovDir[x][y] != old_move_dir)
7065       MovDelay[x][y] = 9;
7066   }
7067   else if (element == EL_BALLOON)
7068   {
7069     MovDir[x][y] = game.wind_direction;
7070     MovDelay[x][y] = 0;
7071   }
7072   else if (element == EL_SPRING)
7073   {
7074 #if USE_NEW_SPRING_BUMPER
7075     if (MovDir[x][y] & MV_HORIZONTAL)
7076     {
7077       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7078           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7079       {
7080         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7081         ResetGfxAnimation(move_x, move_y);
7082         TEST_DrawLevelField(move_x, move_y);
7083
7084         MovDir[x][y] = back_dir;
7085       }
7086       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7087                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7088         MovDir[x][y] = MV_NONE;
7089     }
7090 #else
7091     if (MovDir[x][y] & MV_HORIZONTAL &&
7092         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7093          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7094       MovDir[x][y] = MV_NONE;
7095 #endif
7096
7097     MovDelay[x][y] = 0;
7098   }
7099   else if (element == EL_ROBOT ||
7100            element == EL_SATELLITE ||
7101            element == EL_PENGUIN ||
7102            element == EL_EMC_ANDROID)
7103   {
7104     int attr_x = -1, attr_y = -1;
7105
7106     if (AllPlayersGone)
7107     {
7108       attr_x = ExitX;
7109       attr_y = ExitY;
7110     }
7111     else
7112     {
7113       int i;
7114
7115       for (i = 0; i < MAX_PLAYERS; i++)
7116       {
7117         struct PlayerInfo *player = &stored_player[i];
7118         int jx = player->jx, jy = player->jy;
7119
7120         if (!player->active)
7121           continue;
7122
7123         if (attr_x == -1 ||
7124             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7125         {
7126           attr_x = jx;
7127           attr_y = jy;
7128         }
7129       }
7130     }
7131
7132     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7133         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7134          game.engine_version < VERSION_IDENT(3,1,0,0)))
7135     {
7136       attr_x = ZX;
7137       attr_y = ZY;
7138     }
7139
7140     if (element == EL_PENGUIN)
7141     {
7142       int i;
7143       static int xy[4][2] =
7144       {
7145         { 0, -1 },
7146         { -1, 0 },
7147         { +1, 0 },
7148         { 0, +1 }
7149       };
7150
7151       for (i = 0; i < NUM_DIRECTIONS; i++)
7152       {
7153         int ex = x + xy[i][0];
7154         int ey = y + xy[i][1];
7155
7156         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7157                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7158                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7159                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7160         {
7161           attr_x = ex;
7162           attr_y = ey;
7163           break;
7164         }
7165       }
7166     }
7167
7168     MovDir[x][y] = MV_NONE;
7169     if (attr_x < x)
7170       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7171     else if (attr_x > x)
7172       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7173     if (attr_y < y)
7174       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7175     else if (attr_y > y)
7176       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7177
7178     if (element == EL_ROBOT)
7179     {
7180       int newx, newy;
7181
7182       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7183         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7184       Moving2Blocked(x, y, &newx, &newy);
7185
7186       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7187         MovDelay[x][y] = 8 + 8 * !RND(3);
7188       else
7189         MovDelay[x][y] = 16;
7190     }
7191     else if (element == EL_PENGUIN)
7192     {
7193       int newx, newy;
7194
7195       MovDelay[x][y] = 1;
7196
7197       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7198       {
7199         boolean first_horiz = RND(2);
7200         int new_move_dir = MovDir[x][y];
7201
7202         MovDir[x][y] =
7203           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7204         Moving2Blocked(x, y, &newx, &newy);
7205
7206         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7207           return;
7208
7209         MovDir[x][y] =
7210           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7211         Moving2Blocked(x, y, &newx, &newy);
7212
7213         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7214           return;
7215
7216         MovDir[x][y] = old_move_dir;
7217         return;
7218       }
7219     }
7220     else if (element == EL_SATELLITE)
7221     {
7222       int newx, newy;
7223
7224       MovDelay[x][y] = 1;
7225
7226       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7227       {
7228         boolean first_horiz = RND(2);
7229         int new_move_dir = MovDir[x][y];
7230
7231         MovDir[x][y] =
7232           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233         Moving2Blocked(x, y, &newx, &newy);
7234
7235         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7236           return;
7237
7238         MovDir[x][y] =
7239           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240         Moving2Blocked(x, y, &newx, &newy);
7241
7242         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7243           return;
7244
7245         MovDir[x][y] = old_move_dir;
7246         return;
7247       }
7248     }
7249     else if (element == EL_EMC_ANDROID)
7250     {
7251       static int check_pos[16] =
7252       {
7253         -1,             /*  0 => (invalid)          */
7254         7,              /*  1 => MV_LEFT            */
7255         3,              /*  2 => MV_RIGHT           */
7256         -1,             /*  3 => (invalid)          */
7257         1,              /*  4 =>            MV_UP   */
7258         0,              /*  5 => MV_LEFT  | MV_UP   */
7259         2,              /*  6 => MV_RIGHT | MV_UP   */
7260         -1,             /*  7 => (invalid)          */
7261         5,              /*  8 =>            MV_DOWN */
7262         6,              /*  9 => MV_LEFT  | MV_DOWN */
7263         4,              /* 10 => MV_RIGHT | MV_DOWN */
7264         -1,             /* 11 => (invalid)          */
7265         -1,             /* 12 => (invalid)          */
7266         -1,             /* 13 => (invalid)          */
7267         -1,             /* 14 => (invalid)          */
7268         -1,             /* 15 => (invalid)          */
7269       };
7270       static struct
7271       {
7272         int dx, dy;
7273         int dir;
7274       } check_xy[8] =
7275       {
7276         { -1, -1,       MV_LEFT  | MV_UP   },
7277         {  0, -1,                  MV_UP   },
7278         { +1, -1,       MV_RIGHT | MV_UP   },
7279         { +1,  0,       MV_RIGHT           },
7280         { +1, +1,       MV_RIGHT | MV_DOWN },
7281         {  0, +1,                  MV_DOWN },
7282         { -1, +1,       MV_LEFT  | MV_DOWN },
7283         { -1,  0,       MV_LEFT            },
7284       };
7285       int start_pos, check_order;
7286       boolean can_clone = FALSE;
7287       int i;
7288
7289       /* check if there is any free field around current position */
7290       for (i = 0; i < 8; i++)
7291       {
7292         int newx = x + check_xy[i].dx;
7293         int newy = y + check_xy[i].dy;
7294
7295         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7296         {
7297           can_clone = TRUE;
7298
7299           break;
7300         }
7301       }
7302
7303       if (can_clone)            /* randomly find an element to clone */
7304       {
7305         can_clone = FALSE;
7306
7307         start_pos = check_pos[RND(8)];
7308         check_order = (RND(2) ? -1 : +1);
7309
7310         for (i = 0; i < 8; i++)
7311         {
7312           int pos_raw = start_pos + i * check_order;
7313           int pos = (pos_raw + 8) % 8;
7314           int newx = x + check_xy[pos].dx;
7315           int newy = y + check_xy[pos].dy;
7316
7317           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7318           {
7319             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7320             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7321
7322             Store[x][y] = Feld[newx][newy];
7323
7324             can_clone = TRUE;
7325
7326             break;
7327           }
7328         }
7329       }
7330
7331       if (can_clone)            /* randomly find a direction to move */
7332       {
7333         can_clone = FALSE;
7334
7335         start_pos = check_pos[RND(8)];
7336         check_order = (RND(2) ? -1 : +1);
7337
7338         for (i = 0; i < 8; i++)
7339         {
7340           int pos_raw = start_pos + i * check_order;
7341           int pos = (pos_raw + 8) % 8;
7342           int newx = x + check_xy[pos].dx;
7343           int newy = y + check_xy[pos].dy;
7344           int new_move_dir = check_xy[pos].dir;
7345
7346           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7347           {
7348             MovDir[x][y] = new_move_dir;
7349             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7350
7351             can_clone = TRUE;
7352
7353             break;
7354           }
7355         }
7356       }
7357
7358       if (can_clone)            /* cloning and moving successful */
7359         return;
7360
7361       /* cannot clone -- try to move towards player */
7362
7363       start_pos = check_pos[MovDir[x][y] & 0x0f];
7364       check_order = (RND(2) ? -1 : +1);
7365
7366       for (i = 0; i < 3; i++)
7367       {
7368         /* first check start_pos, then previous/next or (next/previous) pos */
7369         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7370         int pos = (pos_raw + 8) % 8;
7371         int newx = x + check_xy[pos].dx;
7372         int newy = y + check_xy[pos].dy;
7373         int new_move_dir = check_xy[pos].dir;
7374
7375         if (IS_PLAYER(newx, newy))
7376           break;
7377
7378         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7379         {
7380           MovDir[x][y] = new_move_dir;
7381           MovDelay[x][y] = level.android_move_time * 8 + 1;
7382
7383           break;
7384         }
7385       }
7386     }
7387   }
7388   else if (move_pattern == MV_TURNING_LEFT ||
7389            move_pattern == MV_TURNING_RIGHT ||
7390            move_pattern == MV_TURNING_LEFT_RIGHT ||
7391            move_pattern == MV_TURNING_RIGHT_LEFT ||
7392            move_pattern == MV_TURNING_RANDOM ||
7393            move_pattern == MV_ALL_DIRECTIONS)
7394   {
7395     boolean can_turn_left =
7396       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7397     boolean can_turn_right =
7398       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7399
7400     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7401       return;
7402
7403     if (move_pattern == MV_TURNING_LEFT)
7404       MovDir[x][y] = left_dir;
7405     else if (move_pattern == MV_TURNING_RIGHT)
7406       MovDir[x][y] = right_dir;
7407     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7408       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7409     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7410       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7411     else if (move_pattern == MV_TURNING_RANDOM)
7412       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7413                       can_turn_right && !can_turn_left ? right_dir :
7414                       RND(2) ? left_dir : right_dir);
7415     else if (can_turn_left && can_turn_right)
7416       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7417     else if (can_turn_left)
7418       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7419     else if (can_turn_right)
7420       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7421     else
7422       MovDir[x][y] = back_dir;
7423
7424     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7425   }
7426   else if (move_pattern == MV_HORIZONTAL ||
7427            move_pattern == MV_VERTICAL)
7428   {
7429     if (move_pattern & old_move_dir)
7430       MovDir[x][y] = back_dir;
7431     else if (move_pattern == MV_HORIZONTAL)
7432       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7433     else if (move_pattern == MV_VERTICAL)
7434       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7435
7436     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7437   }
7438   else if (move_pattern & MV_ANY_DIRECTION)
7439   {
7440     MovDir[x][y] = move_pattern;
7441     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7442   }
7443   else if (move_pattern & MV_WIND_DIRECTION)
7444   {
7445     MovDir[x][y] = game.wind_direction;
7446     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7447   }
7448   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7449   {
7450     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7451       MovDir[x][y] = left_dir;
7452     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7453       MovDir[x][y] = right_dir;
7454
7455     if (MovDir[x][y] != old_move_dir)
7456       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457   }
7458   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7459   {
7460     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7461       MovDir[x][y] = right_dir;
7462     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7463       MovDir[x][y] = left_dir;
7464
7465     if (MovDir[x][y] != old_move_dir)
7466       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7467   }
7468   else if (move_pattern == MV_TOWARDS_PLAYER ||
7469            move_pattern == MV_AWAY_FROM_PLAYER)
7470   {
7471     int attr_x = -1, attr_y = -1;
7472     int newx, newy;
7473     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7474
7475     if (AllPlayersGone)
7476     {
7477       attr_x = ExitX;
7478       attr_y = ExitY;
7479     }
7480     else
7481     {
7482       int i;
7483
7484       for (i = 0; i < MAX_PLAYERS; i++)
7485       {
7486         struct PlayerInfo *player = &stored_player[i];
7487         int jx = player->jx, jy = player->jy;
7488
7489         if (!player->active)
7490           continue;
7491
7492         if (attr_x == -1 ||
7493             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7494         {
7495           attr_x = jx;
7496           attr_y = jy;
7497         }
7498       }
7499     }
7500
7501     MovDir[x][y] = MV_NONE;
7502     if (attr_x < x)
7503       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7504     else if (attr_x > x)
7505       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7506     if (attr_y < y)
7507       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7508     else if (attr_y > y)
7509       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7510
7511     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7512
7513     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7514     {
7515       boolean first_horiz = RND(2);
7516       int new_move_dir = MovDir[x][y];
7517
7518       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7519       {
7520         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7521         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522
7523         return;
7524       }
7525
7526       MovDir[x][y] =
7527         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7528       Moving2Blocked(x, y, &newx, &newy);
7529
7530       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7531         return;
7532
7533       MovDir[x][y] =
7534         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535       Moving2Blocked(x, y, &newx, &newy);
7536
7537       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7538         return;
7539
7540       MovDir[x][y] = old_move_dir;
7541     }
7542   }
7543   else if (move_pattern == MV_WHEN_PUSHED ||
7544            move_pattern == MV_WHEN_DROPPED)
7545   {
7546     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7547       MovDir[x][y] = MV_NONE;
7548
7549     MovDelay[x][y] = 0;
7550   }
7551   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7552   {
7553     static int test_xy[7][2] =
7554     {
7555       { 0, -1 },
7556       { -1, 0 },
7557       { +1, 0 },
7558       { 0, +1 },
7559       { 0, -1 },
7560       { -1, 0 },
7561       { +1, 0 },
7562     };
7563     static int test_dir[7] =
7564     {
7565       MV_UP,
7566       MV_LEFT,
7567       MV_RIGHT,
7568       MV_DOWN,
7569       MV_UP,
7570       MV_LEFT,
7571       MV_RIGHT,
7572     };
7573     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7574     int move_preference = -1000000;     /* start with very low preference */
7575     int new_move_dir = MV_NONE;
7576     int start_test = RND(4);
7577     int i;
7578
7579     for (i = 0; i < NUM_DIRECTIONS; i++)
7580     {
7581       int move_dir = test_dir[start_test + i];
7582       int move_dir_preference;
7583
7584       xx = x + test_xy[start_test + i][0];
7585       yy = y + test_xy[start_test + i][1];
7586
7587       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7588           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7589       {
7590         new_move_dir = move_dir;
7591
7592         break;
7593       }
7594
7595       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7596         continue;
7597
7598       move_dir_preference = -1 * RunnerVisit[xx][yy];
7599       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7600         move_dir_preference = PlayerVisit[xx][yy];
7601
7602       if (move_dir_preference > move_preference)
7603       {
7604         /* prefer field that has not been visited for the longest time */
7605         move_preference = move_dir_preference;
7606         new_move_dir = move_dir;
7607       }
7608       else if (move_dir_preference == move_preference &&
7609                move_dir == old_move_dir)
7610       {
7611         /* prefer last direction when all directions are preferred equally */
7612         move_preference = move_dir_preference;
7613         new_move_dir = move_dir;
7614       }
7615     }
7616
7617     MovDir[x][y] = new_move_dir;
7618     if (old_move_dir != new_move_dir)
7619       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7620   }
7621 }
7622
7623 static void TurnRound(int x, int y)
7624 {
7625   int direction = MovDir[x][y];
7626
7627   TurnRoundExt(x, y);
7628
7629   GfxDir[x][y] = MovDir[x][y];
7630
7631   if (direction != MovDir[x][y])
7632     GfxFrame[x][y] = 0;
7633
7634   if (MovDelay[x][y])
7635     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7636
7637   ResetGfxFrame(x, y, FALSE);
7638 }
7639
7640 static boolean JustBeingPushed(int x, int y)
7641 {
7642   int i;
7643
7644   for (i = 0; i < MAX_PLAYERS; i++)
7645   {
7646     struct PlayerInfo *player = &stored_player[i];
7647
7648     if (player->active && player->is_pushing && player->MovPos)
7649     {
7650       int next_jx = player->jx + (player->jx - player->last_jx);
7651       int next_jy = player->jy + (player->jy - player->last_jy);
7652
7653       if (x == next_jx && y == next_jy)
7654         return TRUE;
7655     }
7656   }
7657
7658   return FALSE;
7659 }
7660
7661 void StartMoving(int x, int y)
7662 {
7663   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7664   int element = Feld[x][y];
7665
7666   if (Stop[x][y])
7667     return;
7668
7669   if (MovDelay[x][y] == 0)
7670     GfxAction[x][y] = ACTION_DEFAULT;
7671
7672   if (CAN_FALL(element) && y < lev_fieldy - 1)
7673   {
7674     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7675         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7676       if (JustBeingPushed(x, y))
7677         return;
7678
7679     if (element == EL_QUICKSAND_FULL)
7680     {
7681       if (IS_FREE(x, y + 1))
7682       {
7683         InitMovingField(x, y, MV_DOWN);
7684         started_moving = TRUE;
7685
7686         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7687 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7688         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7689           Store[x][y] = EL_ROCK;
7690 #else
7691         Store[x][y] = EL_ROCK;
7692 #endif
7693
7694         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7695       }
7696       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7697       {
7698         if (!MovDelay[x][y])
7699         {
7700           MovDelay[x][y] = TILEY + 1;
7701
7702           ResetGfxAnimation(x, y);
7703           ResetGfxAnimation(x, y + 1);
7704         }
7705
7706         if (MovDelay[x][y])
7707         {
7708           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7709           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7710
7711           MovDelay[x][y]--;
7712           if (MovDelay[x][y])
7713             return;
7714         }
7715
7716         Feld[x][y] = EL_QUICKSAND_EMPTY;
7717         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7718         Store[x][y + 1] = Store[x][y];
7719         Store[x][y] = 0;
7720
7721         PlayLevelSoundAction(x, y, ACTION_FILLING);
7722       }
7723       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7724       {
7725         if (!MovDelay[x][y])
7726         {
7727           MovDelay[x][y] = TILEY + 1;
7728
7729           ResetGfxAnimation(x, y);
7730           ResetGfxAnimation(x, y + 1);
7731         }
7732
7733         if (MovDelay[x][y])
7734         {
7735           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7736           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7737
7738           MovDelay[x][y]--;
7739           if (MovDelay[x][y])
7740             return;
7741         }
7742
7743         Feld[x][y] = EL_QUICKSAND_EMPTY;
7744         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7745         Store[x][y + 1] = Store[x][y];
7746         Store[x][y] = 0;
7747
7748         PlayLevelSoundAction(x, y, ACTION_FILLING);
7749       }
7750     }
7751     else if (element == EL_QUICKSAND_FAST_FULL)
7752     {
7753       if (IS_FREE(x, y + 1))
7754       {
7755         InitMovingField(x, y, MV_DOWN);
7756         started_moving = TRUE;
7757
7758         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7759 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7760         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7761           Store[x][y] = EL_ROCK;
7762 #else
7763         Store[x][y] = EL_ROCK;
7764 #endif
7765
7766         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7767       }
7768       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7769       {
7770         if (!MovDelay[x][y])
7771         {
7772           MovDelay[x][y] = TILEY + 1;
7773
7774           ResetGfxAnimation(x, y);
7775           ResetGfxAnimation(x, y + 1);
7776         }
7777
7778         if (MovDelay[x][y])
7779         {
7780           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7781           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7782
7783           MovDelay[x][y]--;
7784           if (MovDelay[x][y])
7785             return;
7786         }
7787
7788         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7789         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7790         Store[x][y + 1] = Store[x][y];
7791         Store[x][y] = 0;
7792
7793         PlayLevelSoundAction(x, y, ACTION_FILLING);
7794       }
7795       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7796       {
7797         if (!MovDelay[x][y])
7798         {
7799           MovDelay[x][y] = TILEY + 1;
7800
7801           ResetGfxAnimation(x, y);
7802           ResetGfxAnimation(x, y + 1);
7803         }
7804
7805         if (MovDelay[x][y])
7806         {
7807           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7808           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7809
7810           MovDelay[x][y]--;
7811           if (MovDelay[x][y])
7812             return;
7813         }
7814
7815         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7816         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7817         Store[x][y + 1] = Store[x][y];
7818         Store[x][y] = 0;
7819
7820         PlayLevelSoundAction(x, y, ACTION_FILLING);
7821       }
7822     }
7823     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7824              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7825     {
7826       InitMovingField(x, y, MV_DOWN);
7827       started_moving = TRUE;
7828
7829       Feld[x][y] = EL_QUICKSAND_FILLING;
7830       Store[x][y] = element;
7831
7832       PlayLevelSoundAction(x, y, ACTION_FILLING);
7833     }
7834     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7835              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7836     {
7837       InitMovingField(x, y, MV_DOWN);
7838       started_moving = TRUE;
7839
7840       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7841       Store[x][y] = element;
7842
7843       PlayLevelSoundAction(x, y, ACTION_FILLING);
7844     }
7845     else if (element == EL_MAGIC_WALL_FULL)
7846     {
7847       if (IS_FREE(x, y + 1))
7848       {
7849         InitMovingField(x, y, MV_DOWN);
7850         started_moving = TRUE;
7851
7852         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7853         Store[x][y] = EL_CHANGED(Store[x][y]);
7854       }
7855       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7856       {
7857         if (!MovDelay[x][y])
7858           MovDelay[x][y] = TILEY/4 + 1;
7859
7860         if (MovDelay[x][y])
7861         {
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7868         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7869         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7870         Store[x][y] = 0;
7871       }
7872     }
7873     else if (element == EL_BD_MAGIC_WALL_FULL)
7874     {
7875       if (IS_FREE(x, y + 1))
7876       {
7877         InitMovingField(x, y, MV_DOWN);
7878         started_moving = TRUE;
7879
7880         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7881         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7882       }
7883       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7884       {
7885         if (!MovDelay[x][y])
7886           MovDelay[x][y] = TILEY/4 + 1;
7887
7888         if (MovDelay[x][y])
7889         {
7890           MovDelay[x][y]--;
7891           if (MovDelay[x][y])
7892             return;
7893         }
7894
7895         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7896         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7897         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7898         Store[x][y] = 0;
7899       }
7900     }
7901     else if (element == EL_DC_MAGIC_WALL_FULL)
7902     {
7903       if (IS_FREE(x, y + 1))
7904       {
7905         InitMovingField(x, y, MV_DOWN);
7906         started_moving = TRUE;
7907
7908         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7909         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7910       }
7911       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7912       {
7913         if (!MovDelay[x][y])
7914           MovDelay[x][y] = TILEY/4 + 1;
7915
7916         if (MovDelay[x][y])
7917         {
7918           MovDelay[x][y]--;
7919           if (MovDelay[x][y])
7920             return;
7921         }
7922
7923         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7924         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7925         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7926         Store[x][y] = 0;
7927       }
7928     }
7929     else if ((CAN_PASS_MAGIC_WALL(element) &&
7930               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7931                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7932              (CAN_PASS_DC_MAGIC_WALL(element) &&
7933               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7934
7935     {
7936       InitMovingField(x, y, MV_DOWN);
7937       started_moving = TRUE;
7938
7939       Feld[x][y] =
7940         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7941          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7942          EL_DC_MAGIC_WALL_FILLING);
7943       Store[x][y] = element;
7944     }
7945     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7946     {
7947       SplashAcid(x, y + 1);
7948
7949       InitMovingField(x, y, MV_DOWN);
7950       started_moving = TRUE;
7951
7952       Store[x][y] = EL_ACID;
7953     }
7954     else if (
7955 #if USE_FIX_IMPACT_COLLISION
7956              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7957               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7958 #else
7959              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7960               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7961 #endif
7962              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7963               CAN_FALL(element) && WasJustFalling[x][y] &&
7964               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7965
7966              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7967               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7968               (Feld[x][y + 1] == EL_BLOCKED)))
7969     {
7970       /* this is needed for a special case not covered by calling "Impact()"
7971          from "ContinueMoving()": if an element moves to a tile directly below
7972          another element which was just falling on that tile (which was empty
7973          in the previous frame), the falling element above would just stop
7974          instead of smashing the element below (in previous version, the above
7975          element was just checked for "moving" instead of "falling", resulting
7976          in incorrect smashes caused by horizontal movement of the above
7977          element; also, the case of the player being the element to smash was
7978          simply not covered here... :-/ ) */
7979
7980       CheckCollision[x][y] = 0;
7981       CheckImpact[x][y] = 0;
7982
7983       Impact(x, y);
7984     }
7985     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7986     {
7987       if (MovDir[x][y] == MV_NONE)
7988       {
7989         InitMovingField(x, y, MV_DOWN);
7990         started_moving = TRUE;
7991       }
7992     }
7993     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7994     {
7995       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7996         MovDir[x][y] = MV_DOWN;
7997
7998       InitMovingField(x, y, MV_DOWN);
7999       started_moving = TRUE;
8000     }
8001     else if (element == EL_AMOEBA_DROP)
8002     {
8003       Feld[x][y] = EL_AMOEBA_GROWING;
8004       Store[x][y] = EL_AMOEBA_WET;
8005     }
8006     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8007               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8008              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8009              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8010     {
8011       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8012                                 (IS_FREE(x - 1, y + 1) ||
8013                                  Feld[x - 1][y + 1] == EL_ACID));
8014       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8015                                 (IS_FREE(x + 1, y + 1) ||
8016                                  Feld[x + 1][y + 1] == EL_ACID));
8017       boolean can_fall_any  = (can_fall_left || can_fall_right);
8018       boolean can_fall_both = (can_fall_left && can_fall_right);
8019       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8020
8021 #if USE_NEW_ALL_SLIPPERY
8022       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8023       {
8024         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8025           can_fall_right = FALSE;
8026         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8027           can_fall_left = FALSE;
8028         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8029           can_fall_right = FALSE;
8030         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8031           can_fall_left = FALSE;
8032
8033         can_fall_any  = (can_fall_left || can_fall_right);
8034         can_fall_both = FALSE;
8035       }
8036 #else
8037       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8038       {
8039         if (slippery_type == SLIPPERY_ONLY_LEFT)
8040           can_fall_right = FALSE;
8041         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8042           can_fall_left = FALSE;
8043         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8044           can_fall_right = FALSE;
8045         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8046           can_fall_left = FALSE;
8047
8048         can_fall_any  = (can_fall_left || can_fall_right);
8049         can_fall_both = (can_fall_left && can_fall_right);
8050       }
8051 #endif
8052
8053 #if USE_NEW_ALL_SLIPPERY
8054 #else
8055 #if USE_NEW_SP_SLIPPERY
8056       /* !!! better use the same properties as for custom elements here !!! */
8057       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8058                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8059       {
8060         can_fall_right = FALSE;         /* slip down on left side */
8061         can_fall_both = FALSE;
8062       }
8063 #endif
8064 #endif
8065
8066 #if USE_NEW_ALL_SLIPPERY
8067       if (can_fall_both)
8068       {
8069         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8070           can_fall_right = FALSE;       /* slip down on left side */
8071         else
8072           can_fall_left = !(can_fall_right = RND(2));
8073
8074         can_fall_both = FALSE;
8075       }
8076 #else
8077       if (can_fall_both)
8078       {
8079         if (game.emulation == EMU_BOULDERDASH ||
8080             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8081           can_fall_right = FALSE;       /* slip down on left side */
8082         else
8083           can_fall_left = !(can_fall_right = RND(2));
8084
8085         can_fall_both = FALSE;
8086       }
8087 #endif
8088
8089       if (can_fall_any)
8090       {
8091         /* if not determined otherwise, prefer left side for slipping down */
8092         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8093         started_moving = TRUE;
8094       }
8095     }
8096 #if 0
8097     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8098 #else
8099     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8100 #endif
8101     {
8102       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8103       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8104       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8105       int belt_dir = game.belt_dir[belt_nr];
8106
8107       if ((belt_dir == MV_LEFT  && left_is_free) ||
8108           (belt_dir == MV_RIGHT && right_is_free))
8109       {
8110         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8111
8112         InitMovingField(x, y, belt_dir);
8113         started_moving = TRUE;
8114
8115         Pushed[x][y] = TRUE;
8116         Pushed[nextx][y] = TRUE;
8117
8118         GfxAction[x][y] = ACTION_DEFAULT;
8119       }
8120       else
8121       {
8122         MovDir[x][y] = 0;       /* if element was moving, stop it */
8123       }
8124     }
8125   }
8126
8127   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8128 #if 0
8129   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8130 #else
8131   if (CAN_MOVE(element) && !started_moving)
8132 #endif
8133   {
8134     int move_pattern = element_info[element].move_pattern;
8135     int newx, newy;
8136
8137 #if 0
8138 #if DEBUG
8139     if (MovDir[x][y] == MV_NONE)
8140     {
8141       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8142              x, y, element, element_info[element].token_name);
8143       printf("StartMoving(): This should never happen!\n");
8144     }
8145 #endif
8146 #endif
8147
8148     Moving2Blocked(x, y, &newx, &newy);
8149
8150     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8151       return;
8152
8153     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8154         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8155     {
8156       WasJustMoving[x][y] = 0;
8157       CheckCollision[x][y] = 0;
8158
8159       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8160
8161       if (Feld[x][y] != element)        /* element has changed */
8162         return;
8163     }
8164
8165     if (!MovDelay[x][y])        /* start new movement phase */
8166     {
8167       /* all objects that can change their move direction after each step
8168          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8169
8170       if (element != EL_YAMYAM &&
8171           element != EL_DARK_YAMYAM &&
8172           element != EL_PACMAN &&
8173           !(move_pattern & MV_ANY_DIRECTION) &&
8174           move_pattern != MV_TURNING_LEFT &&
8175           move_pattern != MV_TURNING_RIGHT &&
8176           move_pattern != MV_TURNING_LEFT_RIGHT &&
8177           move_pattern != MV_TURNING_RIGHT_LEFT &&
8178           move_pattern != MV_TURNING_RANDOM)
8179       {
8180         TurnRound(x, y);
8181
8182         if (MovDelay[x][y] && (element == EL_BUG ||
8183                                element == EL_SPACESHIP ||
8184                                element == EL_SP_SNIKSNAK ||
8185                                element == EL_SP_ELECTRON ||
8186                                element == EL_MOLE))
8187           TEST_DrawLevelField(x, y);
8188       }
8189     }
8190
8191     if (MovDelay[x][y])         /* wait some time before next movement */
8192     {
8193       MovDelay[x][y]--;
8194
8195       if (element == EL_ROBOT ||
8196           element == EL_YAMYAM ||
8197           element == EL_DARK_YAMYAM)
8198       {
8199         DrawLevelElementAnimationIfNeeded(x, y, element);
8200         PlayLevelSoundAction(x, y, ACTION_WAITING);
8201       }
8202       else if (element == EL_SP_ELECTRON)
8203         DrawLevelElementAnimationIfNeeded(x, y, element);
8204       else if (element == EL_DRAGON)
8205       {
8206         int i;
8207         int dir = MovDir[x][y];
8208         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8209         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8210         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8211                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8212                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8213                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8214         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8215
8216         GfxAction[x][y] = ACTION_ATTACKING;
8217
8218         if (IS_PLAYER(x, y))
8219           DrawPlayerField(x, y);
8220         else
8221           TEST_DrawLevelField(x, y);
8222
8223         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8224
8225         for (i = 1; i <= 3; i++)
8226         {
8227           int xx = x + i * dx;
8228           int yy = y + i * dy;
8229           int sx = SCREENX(xx);
8230           int sy = SCREENY(yy);
8231           int flame_graphic = graphic + (i - 1);
8232
8233           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8234             break;
8235
8236           if (MovDelay[x][y])
8237           {
8238             int flamed = MovingOrBlocked2Element(xx, yy);
8239
8240             /* !!! */
8241 #if 0
8242             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8243               Bang(xx, yy);
8244             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8245               RemoveMovingField(xx, yy);
8246             else
8247               RemoveField(xx, yy);
8248 #else
8249             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8250               Bang(xx, yy);
8251             else
8252               RemoveMovingField(xx, yy);
8253 #endif
8254
8255             ChangeDelay[xx][yy] = 0;
8256
8257             Feld[xx][yy] = EL_FLAMES;
8258
8259             if (IN_SCR_FIELD(sx, sy))
8260             {
8261               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8262               DrawGraphic(sx, sy, flame_graphic, frame);
8263             }
8264           }
8265           else
8266           {
8267             if (Feld[xx][yy] == EL_FLAMES)
8268               Feld[xx][yy] = EL_EMPTY;
8269             TEST_DrawLevelField(xx, yy);
8270           }
8271         }
8272       }
8273
8274       if (MovDelay[x][y])       /* element still has to wait some time */
8275       {
8276         PlayLevelSoundAction(x, y, ACTION_WAITING);
8277
8278         return;
8279       }
8280     }
8281
8282     /* now make next step */
8283
8284     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8285
8286     if (DONT_COLLIDE_WITH(element) &&
8287         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8288         !PLAYER_ENEMY_PROTECTED(newx, newy))
8289     {
8290       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8291
8292       return;
8293     }
8294
8295     else if (CAN_MOVE_INTO_ACID(element) &&
8296              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8297              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8298              (MovDir[x][y] == MV_DOWN ||
8299               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8300     {
8301       SplashAcid(newx, newy);
8302       Store[x][y] = EL_ACID;
8303     }
8304     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8305     {
8306       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8307           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8308           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8309           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8310       {
8311         RemoveField(x, y);
8312         TEST_DrawLevelField(x, y);
8313
8314         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8315         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8316           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8317
8318         local_player->friends_still_needed--;
8319         if (!local_player->friends_still_needed &&
8320             !local_player->GameOver && AllPlayersGone)
8321           PlayerWins(local_player);
8322
8323         return;
8324       }
8325       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8326       {
8327         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8328           TEST_DrawLevelField(newx, newy);
8329         else
8330           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8331       }
8332       else if (!IS_FREE(newx, newy))
8333       {
8334         GfxAction[x][y] = ACTION_WAITING;
8335
8336         if (IS_PLAYER(x, y))
8337           DrawPlayerField(x, y);
8338         else
8339           TEST_DrawLevelField(x, y);
8340
8341         return;
8342       }
8343     }
8344     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8345     {
8346       if (IS_FOOD_PIG(Feld[newx][newy]))
8347       {
8348         if (IS_MOVING(newx, newy))
8349           RemoveMovingField(newx, newy);
8350         else
8351         {
8352           Feld[newx][newy] = EL_EMPTY;
8353           TEST_DrawLevelField(newx, newy);
8354         }
8355
8356         PlayLevelSound(x, y, SND_PIG_DIGGING);
8357       }
8358       else if (!IS_FREE(newx, newy))
8359       {
8360         if (IS_PLAYER(x, y))
8361           DrawPlayerField(x, y);
8362         else
8363           TEST_DrawLevelField(x, y);
8364
8365         return;
8366       }
8367     }
8368     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8369     {
8370       if (Store[x][y] != EL_EMPTY)
8371       {
8372         boolean can_clone = FALSE;
8373         int xx, yy;
8374
8375         /* check if element to clone is still there */
8376         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8377         {
8378           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8379           {
8380             can_clone = TRUE;
8381
8382             break;
8383           }
8384         }
8385
8386         /* cannot clone or target field not free anymore -- do not clone */
8387         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8388           Store[x][y] = EL_EMPTY;
8389       }
8390
8391       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8392       {
8393         if (IS_MV_DIAGONAL(MovDir[x][y]))
8394         {
8395           int diagonal_move_dir = MovDir[x][y];
8396           int stored = Store[x][y];
8397           int change_delay = 8;
8398           int graphic;
8399
8400           /* android is moving diagonally */
8401
8402           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8403
8404           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8405           GfxElement[x][y] = EL_EMC_ANDROID;
8406           GfxAction[x][y] = ACTION_SHRINKING;
8407           GfxDir[x][y] = diagonal_move_dir;
8408           ChangeDelay[x][y] = change_delay;
8409
8410           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8411                                    GfxDir[x][y]);
8412
8413           DrawLevelGraphicAnimation(x, y, graphic);
8414           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8415
8416           if (Feld[newx][newy] == EL_ACID)
8417           {
8418             SplashAcid(newx, newy);
8419
8420             return;
8421           }
8422
8423           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8424
8425           Store[newx][newy] = EL_EMC_ANDROID;
8426           GfxElement[newx][newy] = EL_EMC_ANDROID;
8427           GfxAction[newx][newy] = ACTION_GROWING;
8428           GfxDir[newx][newy] = diagonal_move_dir;
8429           ChangeDelay[newx][newy] = change_delay;
8430
8431           graphic = el_act_dir2img(GfxElement[newx][newy],
8432                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8433
8434           DrawLevelGraphicAnimation(newx, newy, graphic);
8435           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8436
8437           return;
8438         }
8439         else
8440         {
8441           Feld[newx][newy] = EL_EMPTY;
8442           TEST_DrawLevelField(newx, newy);
8443
8444           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8445         }
8446       }
8447       else if (!IS_FREE(newx, newy))
8448       {
8449 #if 0
8450         if (IS_PLAYER(x, y))
8451           DrawPlayerField(x, y);
8452         else
8453           TEST_DrawLevelField(x, y);
8454 #endif
8455
8456         return;
8457       }
8458     }
8459     else if (IS_CUSTOM_ELEMENT(element) &&
8460              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8461     {
8462 #if 1
8463       if (!DigFieldByCE(newx, newy, element))
8464         return;
8465 #else
8466       int new_element = Feld[newx][newy];
8467
8468       if (!IS_FREE(newx, newy))
8469       {
8470         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8471                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8472                       ACTION_BREAKING);
8473
8474         /* no element can dig solid indestructible elements */
8475         if (IS_INDESTRUCTIBLE(new_element) &&
8476             !IS_DIGGABLE(new_element) &&
8477             !IS_COLLECTIBLE(new_element))
8478           return;
8479
8480         if (AmoebaNr[newx][newy] &&
8481             (new_element == EL_AMOEBA_FULL ||
8482              new_element == EL_BD_AMOEBA ||
8483              new_element == EL_AMOEBA_GROWING))
8484         {
8485           AmoebaCnt[AmoebaNr[newx][newy]]--;
8486           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8487         }
8488
8489         if (IS_MOVING(newx, newy))
8490           RemoveMovingField(newx, newy);
8491         else
8492         {
8493           RemoveField(newx, newy);
8494           TEST_DrawLevelField(newx, newy);
8495         }
8496
8497         /* if digged element was about to explode, prevent the explosion */
8498         ExplodeField[newx][newy] = EX_TYPE_NONE;
8499
8500         PlayLevelSoundAction(x, y, action);
8501       }
8502
8503       Store[newx][newy] = EL_EMPTY;
8504
8505 #if 1
8506       /* this makes it possible to leave the removed element again */
8507       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8508         Store[newx][newy] = new_element;
8509 #else
8510       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8511       {
8512         int move_leave_element = element_info[element].move_leave_element;
8513
8514         /* this makes it possible to leave the removed element again */
8515         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8516                              new_element : move_leave_element);
8517       }
8518 #endif
8519
8520 #endif
8521
8522       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8523       {
8524         RunnerVisit[x][y] = FrameCounter;
8525         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8526       }
8527     }
8528     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8529     {
8530       if (!IS_FREE(newx, newy))
8531       {
8532         if (IS_PLAYER(x, y))
8533           DrawPlayerField(x, y);
8534         else
8535           TEST_DrawLevelField(x, y);
8536
8537         return;
8538       }
8539       else
8540       {
8541         boolean wanna_flame = !RND(10);
8542         int dx = newx - x, dy = newy - y;
8543         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8544         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8545         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8546                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8547         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8548                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8549
8550         if ((wanna_flame ||
8551              IS_CLASSIC_ENEMY(element1) ||
8552              IS_CLASSIC_ENEMY(element2)) &&
8553             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8554             element1 != EL_FLAMES && element2 != EL_FLAMES)
8555         {
8556           ResetGfxAnimation(x, y);
8557           GfxAction[x][y] = ACTION_ATTACKING;
8558
8559           if (IS_PLAYER(x, y))
8560             DrawPlayerField(x, y);
8561           else
8562             TEST_DrawLevelField(x, y);
8563
8564           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8565
8566           MovDelay[x][y] = 50;
8567
8568           /* !!! */
8569 #if 0
8570           RemoveField(newx, newy);
8571 #endif
8572           Feld[newx][newy] = EL_FLAMES;
8573           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8574           {
8575 #if 0
8576             RemoveField(newx1, newy1);
8577 #endif
8578             Feld[newx1][newy1] = EL_FLAMES;
8579           }
8580           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8581           {
8582 #if 0
8583             RemoveField(newx2, newy2);
8584 #endif
8585             Feld[newx2][newy2] = EL_FLAMES;
8586           }
8587
8588           return;
8589         }
8590       }
8591     }
8592     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8593              Feld[newx][newy] == EL_DIAMOND)
8594     {
8595       if (IS_MOVING(newx, newy))
8596         RemoveMovingField(newx, newy);
8597       else
8598       {
8599         Feld[newx][newy] = EL_EMPTY;
8600         TEST_DrawLevelField(newx, newy);
8601       }
8602
8603       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8604     }
8605     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8606              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8607     {
8608       if (AmoebaNr[newx][newy])
8609       {
8610         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8611         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8612             Feld[newx][newy] == EL_BD_AMOEBA)
8613           AmoebaCnt[AmoebaNr[newx][newy]]--;
8614       }
8615
8616 #if 0
8617       /* !!! test !!! */
8618       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8619       {
8620         RemoveMovingField(newx, newy);
8621       }
8622 #else
8623       if (IS_MOVING(newx, newy))
8624       {
8625         RemoveMovingField(newx, newy);
8626       }
8627 #endif
8628       else
8629       {
8630         Feld[newx][newy] = EL_EMPTY;
8631         TEST_DrawLevelField(newx, newy);
8632       }
8633
8634       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8635     }
8636     else if ((element == EL_PACMAN || element == EL_MOLE)
8637              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8638     {
8639       if (AmoebaNr[newx][newy])
8640       {
8641         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8642         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8643             Feld[newx][newy] == EL_BD_AMOEBA)
8644           AmoebaCnt[AmoebaNr[newx][newy]]--;
8645       }
8646
8647       if (element == EL_MOLE)
8648       {
8649         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8650         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8651
8652         ResetGfxAnimation(x, y);
8653         GfxAction[x][y] = ACTION_DIGGING;
8654         TEST_DrawLevelField(x, y);
8655
8656         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8657
8658         return;                         /* wait for shrinking amoeba */
8659       }
8660       else      /* element == EL_PACMAN */
8661       {
8662         Feld[newx][newy] = EL_EMPTY;
8663         TEST_DrawLevelField(newx, newy);
8664         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8665       }
8666     }
8667     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8668              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8669               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8670     {
8671       /* wait for shrinking amoeba to completely disappear */
8672       return;
8673     }
8674     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8675     {
8676       /* object was running against a wall */
8677
8678       TurnRound(x, y);
8679
8680 #if 0
8681       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8682       if (move_pattern & MV_ANY_DIRECTION &&
8683           move_pattern == MovDir[x][y])
8684       {
8685         int blocking_element =
8686           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8687
8688         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8689                                  MovDir[x][y]);
8690
8691         element = Feld[x][y];   /* element might have changed */
8692       }
8693 #endif
8694
8695       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8696         DrawLevelElementAnimation(x, y, element);
8697
8698       if (DONT_TOUCH(element))
8699         TestIfBadThingTouchesPlayer(x, y);
8700
8701       return;
8702     }
8703
8704     InitMovingField(x, y, MovDir[x][y]);
8705
8706     PlayLevelSoundAction(x, y, ACTION_MOVING);
8707   }
8708
8709   if (MovDir[x][y])
8710     ContinueMoving(x, y);
8711 }
8712
8713 void ContinueMoving(int x, int y)
8714 {
8715   int element = Feld[x][y];
8716   struct ElementInfo *ei = &element_info[element];
8717   int direction = MovDir[x][y];
8718   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8719   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8720   int newx = x + dx, newy = y + dy;
8721   int stored = Store[x][y];
8722   int stored_new = Store[newx][newy];
8723   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8724   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8725   boolean last_line = (newy == lev_fieldy - 1);
8726
8727   MovPos[x][y] += getElementMoveStepsize(x, y);
8728
8729   if (pushed_by_player) /* special case: moving object pushed by player */
8730     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8731
8732   if (ABS(MovPos[x][y]) < TILEX)
8733   {
8734 #if 0
8735     int ee = Feld[x][y];
8736     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8737     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8738
8739     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8740            x, y, ABS(MovPos[x][y]),
8741            ee, gg, ff,
8742            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8743 #endif
8744
8745     TEST_DrawLevelField(x, y);
8746
8747     return;     /* element is still moving */
8748   }
8749
8750   /* element reached destination field */
8751
8752   Feld[x][y] = EL_EMPTY;
8753   Feld[newx][newy] = element;
8754   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8755
8756   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8757   {
8758     element = Feld[newx][newy] = EL_ACID;
8759   }
8760   else if (element == EL_MOLE)
8761   {
8762     Feld[x][y] = EL_SAND;
8763
8764     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8765   }
8766   else if (element == EL_QUICKSAND_FILLING)
8767   {
8768     element = Feld[newx][newy] = get_next_element(element);
8769     Store[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_QUICKSAND_EMPTYING)
8772   {
8773     Feld[x][y] = get_next_element(element);
8774     element = Feld[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_QUICKSAND_FAST_FILLING)
8777   {
8778     element = Feld[newx][newy] = get_next_element(element);
8779     Store[newx][newy] = Store[x][y];
8780   }
8781   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8782   {
8783     Feld[x][y] = get_next_element(element);
8784     element = Feld[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_MAGIC_WALL_FILLING)
8787   {
8788     element = Feld[newx][newy] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8791     Store[newx][newy] = Store[x][y];
8792   }
8793   else if (element == EL_MAGIC_WALL_EMPTYING)
8794   {
8795     Feld[x][y] = get_next_element(element);
8796     if (!game.magic_wall_active)
8797       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8798     element = Feld[newx][newy] = Store[x][y];
8799
8800 #if USE_NEW_CUSTOM_VALUE
8801     InitField(newx, newy, FALSE);
8802 #endif
8803   }
8804   else if (element == EL_BD_MAGIC_WALL_FILLING)
8805   {
8806     element = Feld[newx][newy] = get_next_element(element);
8807     if (!game.magic_wall_active)
8808       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8809     Store[newx][newy] = Store[x][y];
8810   }
8811   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8812   {
8813     Feld[x][y] = get_next_element(element);
8814     if (!game.magic_wall_active)
8815       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8816     element = Feld[newx][newy] = Store[x][y];
8817
8818 #if USE_NEW_CUSTOM_VALUE
8819     InitField(newx, newy, FALSE);
8820 #endif
8821   }
8822   else if (element == EL_DC_MAGIC_WALL_FILLING)
8823   {
8824     element = Feld[newx][newy] = get_next_element(element);
8825     if (!game.magic_wall_active)
8826       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8827     Store[newx][newy] = Store[x][y];
8828   }
8829   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8830   {
8831     Feld[x][y] = get_next_element(element);
8832     if (!game.magic_wall_active)
8833       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8834     element = Feld[newx][newy] = Store[x][y];
8835
8836 #if USE_NEW_CUSTOM_VALUE
8837     InitField(newx, newy, FALSE);
8838 #endif
8839   }
8840   else if (element == EL_AMOEBA_DROPPING)
8841   {
8842     Feld[x][y] = get_next_element(element);
8843     element = Feld[newx][newy] = Store[x][y];
8844   }
8845   else if (element == EL_SOKOBAN_OBJECT)
8846   {
8847     if (Back[x][y])
8848       Feld[x][y] = Back[x][y];
8849
8850     if (Back[newx][newy])
8851       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8852
8853     Back[x][y] = Back[newx][newy] = 0;
8854   }
8855
8856   Store[x][y] = EL_EMPTY;
8857   MovPos[x][y] = 0;
8858   MovDir[x][y] = 0;
8859   MovDelay[x][y] = 0;
8860
8861   MovDelay[newx][newy] = 0;
8862
8863   if (CAN_CHANGE_OR_HAS_ACTION(element))
8864   {
8865     /* copy element change control values to new field */
8866     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8867     ChangePage[newx][newy]  = ChangePage[x][y];
8868     ChangeCount[newx][newy] = ChangeCount[x][y];
8869     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8870   }
8871
8872 #if USE_NEW_CUSTOM_VALUE
8873   CustomValue[newx][newy] = CustomValue[x][y];
8874 #endif
8875
8876   ChangeDelay[x][y] = 0;
8877   ChangePage[x][y] = -1;
8878   ChangeCount[x][y] = 0;
8879   ChangeEvent[x][y] = -1;
8880
8881 #if USE_NEW_CUSTOM_VALUE
8882   CustomValue[x][y] = 0;
8883 #endif
8884
8885   /* copy animation control values to new field */
8886   GfxFrame[newx][newy]  = GfxFrame[x][y];
8887   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8888   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8889   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8890
8891   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8892
8893   /* some elements can leave other elements behind after moving */
8894 #if 1
8895   if (ei->move_leave_element != EL_EMPTY &&
8896       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8897       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8898 #else
8899   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8900       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8901       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8902 #endif
8903   {
8904     int move_leave_element = ei->move_leave_element;
8905
8906 #if 1
8907 #if 1
8908     /* this makes it possible to leave the removed element again */
8909     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8910       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8911 #else
8912     /* this makes it possible to leave the removed element again */
8913     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8914       move_leave_element = stored;
8915 #endif
8916 #else
8917     /* this makes it possible to leave the removed element again */
8918     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8919         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8920       move_leave_element = stored;
8921 #endif
8922
8923     Feld[x][y] = move_leave_element;
8924
8925     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8926       MovDir[x][y] = direction;
8927
8928     InitField(x, y, FALSE);
8929
8930     if (GFX_CRUMBLED(Feld[x][y]))
8931       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8932
8933     if (ELEM_IS_PLAYER(move_leave_element))
8934       RelocatePlayer(x, y, move_leave_element);
8935   }
8936
8937   /* do this after checking for left-behind element */
8938   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8939
8940   if (!CAN_MOVE(element) ||
8941       (CAN_FALL(element) && direction == MV_DOWN &&
8942        (element == EL_SPRING ||
8943         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8944         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8945     GfxDir[x][y] = MovDir[newx][newy] = 0;
8946
8947   TEST_DrawLevelField(x, y);
8948   TEST_DrawLevelField(newx, newy);
8949
8950   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8951
8952   /* prevent pushed element from moving on in pushed direction */
8953   if (pushed_by_player && CAN_MOVE(element) &&
8954       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8955       !(element_info[element].move_pattern & direction))
8956     TurnRound(newx, newy);
8957
8958   /* prevent elements on conveyor belt from moving on in last direction */
8959   if (pushed_by_conveyor && CAN_FALL(element) &&
8960       direction & MV_HORIZONTAL)
8961     MovDir[newx][newy] = 0;
8962
8963   if (!pushed_by_player)
8964   {
8965     int nextx = newx + dx, nexty = newy + dy;
8966     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8967
8968     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8969
8970     if (CAN_FALL(element) && direction == MV_DOWN)
8971       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8972
8973     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8974       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8975
8976 #if USE_FIX_IMPACT_COLLISION
8977     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8978       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8979 #endif
8980   }
8981
8982   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8983   {
8984     TestIfBadThingTouchesPlayer(newx, newy);
8985     TestIfBadThingTouchesFriend(newx, newy);
8986
8987     if (!IS_CUSTOM_ELEMENT(element))
8988       TestIfBadThingTouchesOtherBadThing(newx, newy);
8989   }
8990   else if (element == EL_PENGUIN)
8991     TestIfFriendTouchesBadThing(newx, newy);
8992
8993   if (DONT_GET_HIT_BY(element))
8994   {
8995     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8996   }
8997
8998   /* give the player one last chance (one more frame) to move away */
8999   if (CAN_FALL(element) && direction == MV_DOWN &&
9000       (last_line || (!IS_FREE(x, newy + 1) &&
9001                      (!IS_PLAYER(x, newy + 1) ||
9002                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9003     Impact(x, newy);
9004
9005   if (pushed_by_player && !game.use_change_when_pushing_bug)
9006   {
9007     int push_side = MV_DIR_OPPOSITE(direction);
9008     struct PlayerInfo *player = PLAYERINFO(x, y);
9009
9010     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9011                                player->index_bit, push_side);
9012     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9013                                         player->index_bit, push_side);
9014   }
9015
9016   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9017     MovDelay[newx][newy] = 1;
9018
9019   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9020
9021   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9022
9023 #if 0
9024   if (ChangePage[newx][newy] != -1)             /* delayed change */
9025   {
9026     int page = ChangePage[newx][newy];
9027     struct ElementChangeInfo *change = &ei->change_page[page];
9028
9029     ChangePage[newx][newy] = -1;
9030
9031     if (change->can_change)
9032     {
9033       if (ChangeElement(newx, newy, element, page))
9034       {
9035         if (change->post_change_function)
9036           change->post_change_function(newx, newy);
9037       }
9038     }
9039
9040     if (change->has_action)
9041       ExecuteCustomElementAction(newx, newy, element, page);
9042   }
9043 #endif
9044
9045   TestIfElementHitsCustomElement(newx, newy, direction);
9046   TestIfPlayerTouchesCustomElement(newx, newy);
9047   TestIfElementTouchesCustomElement(newx, newy);
9048
9049   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9050       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9051     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9052                              MV_DIR_OPPOSITE(direction));
9053 }
9054
9055 int AmoebeNachbarNr(int ax, int ay)
9056 {
9057   int i;
9058   int element = Feld[ax][ay];
9059   int group_nr = 0;
9060   static int xy[4][2] =
9061   {
9062     { 0, -1 },
9063     { -1, 0 },
9064     { +1, 0 },
9065     { 0, +1 }
9066   };
9067
9068   for (i = 0; i < NUM_DIRECTIONS; i++)
9069   {
9070     int x = ax + xy[i][0];
9071     int y = ay + xy[i][1];
9072
9073     if (!IN_LEV_FIELD(x, y))
9074       continue;
9075
9076     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9077       group_nr = AmoebaNr[x][y];
9078   }
9079
9080   return group_nr;
9081 }
9082
9083 void AmoebenVereinigen(int ax, int ay)
9084 {
9085   int i, x, y, xx, yy;
9086   int new_group_nr = AmoebaNr[ax][ay];
9087   static int xy[4][2] =
9088   {
9089     { 0, -1 },
9090     { -1, 0 },
9091     { +1, 0 },
9092     { 0, +1 }
9093   };
9094
9095   if (new_group_nr == 0)
9096     return;
9097
9098   for (i = 0; i < NUM_DIRECTIONS; i++)
9099   {
9100     x = ax + xy[i][0];
9101     y = ay + xy[i][1];
9102
9103     if (!IN_LEV_FIELD(x, y))
9104       continue;
9105
9106     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9107          Feld[x][y] == EL_BD_AMOEBA ||
9108          Feld[x][y] == EL_AMOEBA_DEAD) &&
9109         AmoebaNr[x][y] != new_group_nr)
9110     {
9111       int old_group_nr = AmoebaNr[x][y];
9112
9113       if (old_group_nr == 0)
9114         return;
9115
9116       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9117       AmoebaCnt[old_group_nr] = 0;
9118       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9119       AmoebaCnt2[old_group_nr] = 0;
9120
9121       SCAN_PLAYFIELD(xx, yy)
9122       {
9123         if (AmoebaNr[xx][yy] == old_group_nr)
9124           AmoebaNr[xx][yy] = new_group_nr;
9125       }
9126     }
9127   }
9128 }
9129
9130 void AmoebeUmwandeln(int ax, int ay)
9131 {
9132   int i, x, y;
9133
9134   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9135   {
9136     int group_nr = AmoebaNr[ax][ay];
9137
9138 #ifdef DEBUG
9139     if (group_nr == 0)
9140     {
9141       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9142       printf("AmoebeUmwandeln(): This should never happen!\n");
9143       return;
9144     }
9145 #endif
9146
9147     SCAN_PLAYFIELD(x, y)
9148     {
9149       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9150       {
9151         AmoebaNr[x][y] = 0;
9152         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9153       }
9154     }
9155
9156     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9157                             SND_AMOEBA_TURNING_TO_GEM :
9158                             SND_AMOEBA_TURNING_TO_ROCK));
9159     Bang(ax, ay);
9160   }
9161   else
9162   {
9163     static int xy[4][2] =
9164     {
9165       { 0, -1 },
9166       { -1, 0 },
9167       { +1, 0 },
9168       { 0, +1 }
9169     };
9170
9171     for (i = 0; i < NUM_DIRECTIONS; i++)
9172     {
9173       x = ax + xy[i][0];
9174       y = ay + xy[i][1];
9175
9176       if (!IN_LEV_FIELD(x, y))
9177         continue;
9178
9179       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9180       {
9181         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9182                               SND_AMOEBA_TURNING_TO_GEM :
9183                               SND_AMOEBA_TURNING_TO_ROCK));
9184         Bang(x, y);
9185       }
9186     }
9187   }
9188 }
9189
9190 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9191 {
9192   int x, y;
9193   int group_nr = AmoebaNr[ax][ay];
9194   boolean done = FALSE;
9195
9196 #ifdef DEBUG
9197   if (group_nr == 0)
9198   {
9199     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9200     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9201     return;
9202   }
9203 #endif
9204
9205   SCAN_PLAYFIELD(x, y)
9206   {
9207     if (AmoebaNr[x][y] == group_nr &&
9208         (Feld[x][y] == EL_AMOEBA_DEAD ||
9209          Feld[x][y] == EL_BD_AMOEBA ||
9210          Feld[x][y] == EL_AMOEBA_GROWING))
9211     {
9212       AmoebaNr[x][y] = 0;
9213       Feld[x][y] = new_element;
9214       InitField(x, y, FALSE);
9215       TEST_DrawLevelField(x, y);
9216       done = TRUE;
9217     }
9218   }
9219
9220   if (done)
9221     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9222                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9223                             SND_BD_AMOEBA_TURNING_TO_GEM));
9224 }
9225
9226 void AmoebeWaechst(int x, int y)
9227 {
9228   static unsigned long sound_delay = 0;
9229   static unsigned long sound_delay_value = 0;
9230
9231   if (!MovDelay[x][y])          /* start new growing cycle */
9232   {
9233     MovDelay[x][y] = 7;
9234
9235     if (DelayReached(&sound_delay, sound_delay_value))
9236     {
9237       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9238       sound_delay_value = 30;
9239     }
9240   }
9241
9242   if (MovDelay[x][y])           /* wait some time before growing bigger */
9243   {
9244     MovDelay[x][y]--;
9245     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9246     {
9247       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9248                                            6 - MovDelay[x][y]);
9249
9250       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9251     }
9252
9253     if (!MovDelay[x][y])
9254     {
9255       Feld[x][y] = Store[x][y];
9256       Store[x][y] = 0;
9257       TEST_DrawLevelField(x, y);
9258     }
9259   }
9260 }
9261
9262 void AmoebaDisappearing(int x, int y)
9263 {
9264   static unsigned long sound_delay = 0;
9265   static unsigned long sound_delay_value = 0;
9266
9267   if (!MovDelay[x][y])          /* start new shrinking cycle */
9268   {
9269     MovDelay[x][y] = 7;
9270
9271     if (DelayReached(&sound_delay, sound_delay_value))
9272       sound_delay_value = 30;
9273   }
9274
9275   if (MovDelay[x][y])           /* wait some time before shrinking */
9276   {
9277     MovDelay[x][y]--;
9278     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9279     {
9280       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9281                                            6 - MovDelay[x][y]);
9282
9283       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9284     }
9285
9286     if (!MovDelay[x][y])
9287     {
9288       Feld[x][y] = EL_EMPTY;
9289       TEST_DrawLevelField(x, y);
9290
9291       /* don't let mole enter this field in this cycle;
9292          (give priority to objects falling to this field from above) */
9293       Stop[x][y] = TRUE;
9294     }
9295   }
9296 }
9297
9298 void AmoebeAbleger(int ax, int ay)
9299 {
9300   int i;
9301   int element = Feld[ax][ay];
9302   int graphic = el2img(element);
9303   int newax = ax, neway = ay;
9304   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9305   static int xy[4][2] =
9306   {
9307     { 0, -1 },
9308     { -1, 0 },
9309     { +1, 0 },
9310     { 0, +1 }
9311   };
9312
9313   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9314   {
9315     Feld[ax][ay] = EL_AMOEBA_DEAD;
9316     TEST_DrawLevelField(ax, ay);
9317     return;
9318   }
9319
9320   if (IS_ANIMATED(graphic))
9321     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9322
9323   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9324     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9325
9326   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9327   {
9328     MovDelay[ax][ay]--;
9329     if (MovDelay[ax][ay])
9330       return;
9331   }
9332
9333   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9334   {
9335     int start = RND(4);
9336     int x = ax + xy[start][0];
9337     int y = ay + xy[start][1];
9338
9339     if (!IN_LEV_FIELD(x, y))
9340       return;
9341
9342     if (IS_FREE(x, y) ||
9343         CAN_GROW_INTO(Feld[x][y]) ||
9344         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9345         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9346     {
9347       newax = x;
9348       neway = y;
9349     }
9350
9351     if (newax == ax && neway == ay)
9352       return;
9353   }
9354   else                          /* normal or "filled" (BD style) amoeba */
9355   {
9356     int start = RND(4);
9357     boolean waiting_for_player = FALSE;
9358
9359     for (i = 0; i < NUM_DIRECTIONS; i++)
9360     {
9361       int j = (start + i) % 4;
9362       int x = ax + xy[j][0];
9363       int y = ay + xy[j][1];
9364
9365       if (!IN_LEV_FIELD(x, y))
9366         continue;
9367
9368       if (IS_FREE(x, y) ||
9369           CAN_GROW_INTO(Feld[x][y]) ||
9370           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9371           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9372       {
9373         newax = x;
9374         neway = y;
9375         break;
9376       }
9377       else if (IS_PLAYER(x, y))
9378         waiting_for_player = TRUE;
9379     }
9380
9381     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9382     {
9383       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9384       {
9385         Feld[ax][ay] = EL_AMOEBA_DEAD;
9386         TEST_DrawLevelField(ax, ay);
9387         AmoebaCnt[AmoebaNr[ax][ay]]--;
9388
9389         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9390         {
9391           if (element == EL_AMOEBA_FULL)
9392             AmoebeUmwandeln(ax, ay);
9393           else if (element == EL_BD_AMOEBA)
9394             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9395         }
9396       }
9397       return;
9398     }
9399     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9400     {
9401       /* amoeba gets larger by growing in some direction */
9402
9403       int new_group_nr = AmoebaNr[ax][ay];
9404
9405 #ifdef DEBUG
9406   if (new_group_nr == 0)
9407   {
9408     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9409     printf("AmoebeAbleger(): This should never happen!\n");
9410     return;
9411   }
9412 #endif
9413
9414       AmoebaNr[newax][neway] = new_group_nr;
9415       AmoebaCnt[new_group_nr]++;
9416       AmoebaCnt2[new_group_nr]++;
9417
9418       /* if amoeba touches other amoeba(s) after growing, unify them */
9419       AmoebenVereinigen(newax, neway);
9420
9421       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9422       {
9423         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9424         return;
9425       }
9426     }
9427   }
9428
9429   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9430       (neway == lev_fieldy - 1 && newax != ax))
9431   {
9432     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9433     Store[newax][neway] = element;
9434   }
9435   else if (neway == ay || element == EL_EMC_DRIPPER)
9436   {
9437     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9438
9439     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9440   }
9441   else
9442   {
9443     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9444     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9445     Store[ax][ay] = EL_AMOEBA_DROP;
9446     ContinueMoving(ax, ay);
9447     return;
9448   }
9449
9450   TEST_DrawLevelField(newax, neway);
9451 }
9452
9453 void Life(int ax, int ay)
9454 {
9455   int x1, y1, x2, y2;
9456   int life_time = 40;
9457   int element = Feld[ax][ay];
9458   int graphic = el2img(element);
9459   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9460                          level.biomaze);
9461   boolean changed = FALSE;
9462
9463   if (IS_ANIMATED(graphic))
9464     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9465
9466   if (Stop[ax][ay])
9467     return;
9468
9469   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9470     MovDelay[ax][ay] = life_time;
9471
9472   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9473   {
9474     MovDelay[ax][ay]--;
9475     if (MovDelay[ax][ay])
9476       return;
9477   }
9478
9479   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9480   {
9481     int xx = ax+x1, yy = ay+y1;
9482     int nachbarn = 0;
9483
9484     if (!IN_LEV_FIELD(xx, yy))
9485       continue;
9486
9487     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9488     {
9489       int x = xx+x2, y = yy+y2;
9490
9491       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9492         continue;
9493
9494       if (((Feld[x][y] == element ||
9495             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9496            !Stop[x][y]) ||
9497           (IS_FREE(x, y) && Stop[x][y]))
9498         nachbarn++;
9499     }
9500
9501     if (xx == ax && yy == ay)           /* field in the middle */
9502     {
9503       if (nachbarn < life_parameter[0] ||
9504           nachbarn > life_parameter[1])
9505       {
9506         Feld[xx][yy] = EL_EMPTY;
9507         if (!Stop[xx][yy])
9508           TEST_DrawLevelField(xx, yy);
9509         Stop[xx][yy] = TRUE;
9510         changed = TRUE;
9511       }
9512     }
9513     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9514     {                                   /* free border field */
9515       if (nachbarn >= life_parameter[2] &&
9516           nachbarn <= life_parameter[3])
9517       {
9518         Feld[xx][yy] = element;
9519         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9520         if (!Stop[xx][yy])
9521           TEST_DrawLevelField(xx, yy);
9522         Stop[xx][yy] = TRUE;
9523         changed = TRUE;
9524       }
9525     }
9526   }
9527
9528   if (changed)
9529     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9530                    SND_GAME_OF_LIFE_GROWING);
9531 }
9532
9533 static void InitRobotWheel(int x, int y)
9534 {
9535   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9536 }
9537
9538 static void RunRobotWheel(int x, int y)
9539 {
9540   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9541 }
9542
9543 static void StopRobotWheel(int x, int y)
9544 {
9545   if (ZX == x && ZY == y)
9546   {
9547     ZX = ZY = -1;
9548
9549     game.robot_wheel_active = FALSE;
9550   }
9551 }
9552
9553 static void InitTimegateWheel(int x, int y)
9554 {
9555   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9556 }
9557
9558 static void RunTimegateWheel(int x, int y)
9559 {
9560   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9561 }
9562
9563 static void InitMagicBallDelay(int x, int y)
9564 {
9565 #if 1
9566   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9567 #else
9568   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9569 #endif
9570 }
9571
9572 static void ActivateMagicBall(int bx, int by)
9573 {
9574   int x, y;
9575
9576   if (level.ball_random)
9577   {
9578     int pos_border = RND(8);    /* select one of the eight border elements */
9579     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9580     int xx = pos_content % 3;
9581     int yy = pos_content / 3;
9582
9583     x = bx - 1 + xx;
9584     y = by - 1 + yy;
9585
9586     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9587       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9588   }
9589   else
9590   {
9591     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9592     {
9593       int xx = x - bx + 1;
9594       int yy = y - by + 1;
9595
9596       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9597         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9598     }
9599   }
9600
9601   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9602 }
9603
9604 void CheckExit(int x, int y)
9605 {
9606   if (local_player->gems_still_needed > 0 ||
9607       local_player->sokobanfields_still_needed > 0 ||
9608       local_player->lights_still_needed > 0)
9609   {
9610     int element = Feld[x][y];
9611     int graphic = el2img(element);
9612
9613     if (IS_ANIMATED(graphic))
9614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9615
9616     return;
9617   }
9618
9619   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9620     return;
9621
9622   Feld[x][y] = EL_EXIT_OPENING;
9623
9624   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9625 }
9626
9627 void CheckExitEM(int x, int y)
9628 {
9629   if (local_player->gems_still_needed > 0 ||
9630       local_player->sokobanfields_still_needed > 0 ||
9631       local_player->lights_still_needed > 0)
9632   {
9633     int element = Feld[x][y];
9634     int graphic = el2img(element);
9635
9636     if (IS_ANIMATED(graphic))
9637       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9638
9639     return;
9640   }
9641
9642   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9643     return;
9644
9645   Feld[x][y] = EL_EM_EXIT_OPENING;
9646
9647   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9648 }
9649
9650 void CheckExitSteel(int x, int y)
9651 {
9652   if (local_player->gems_still_needed > 0 ||
9653       local_player->sokobanfields_still_needed > 0 ||
9654       local_player->lights_still_needed > 0)
9655   {
9656     int element = Feld[x][y];
9657     int graphic = el2img(element);
9658
9659     if (IS_ANIMATED(graphic))
9660       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9661
9662     return;
9663   }
9664
9665   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9666     return;
9667
9668   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9669
9670   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9671 }
9672
9673 void CheckExitSteelEM(int x, int y)
9674 {
9675   if (local_player->gems_still_needed > 0 ||
9676       local_player->sokobanfields_still_needed > 0 ||
9677       local_player->lights_still_needed > 0)
9678   {
9679     int element = Feld[x][y];
9680     int graphic = el2img(element);
9681
9682     if (IS_ANIMATED(graphic))
9683       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9684
9685     return;
9686   }
9687
9688   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9689     return;
9690
9691   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9692
9693   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9694 }
9695
9696 void CheckExitSP(int x, int y)
9697 {
9698   if (local_player->gems_still_needed > 0)
9699   {
9700     int element = Feld[x][y];
9701     int graphic = el2img(element);
9702
9703     if (IS_ANIMATED(graphic))
9704       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9705
9706     return;
9707   }
9708
9709   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9710     return;
9711
9712   Feld[x][y] = EL_SP_EXIT_OPENING;
9713
9714   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9715 }
9716
9717 static void CloseAllOpenTimegates()
9718 {
9719   int x, y;
9720
9721   SCAN_PLAYFIELD(x, y)
9722   {
9723     int element = Feld[x][y];
9724
9725     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9726     {
9727       Feld[x][y] = EL_TIMEGATE_CLOSING;
9728
9729       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9730     }
9731   }
9732 }
9733
9734 void DrawTwinkleOnField(int x, int y)
9735 {
9736   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9737     return;
9738
9739   if (Feld[x][y] == EL_BD_DIAMOND)
9740     return;
9741
9742   if (MovDelay[x][y] == 0)      /* next animation frame */
9743     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9744
9745   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9746   {
9747     MovDelay[x][y]--;
9748
9749     DrawLevelElementAnimation(x, y, Feld[x][y]);
9750
9751     if (MovDelay[x][y] != 0)
9752     {
9753       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9754                                            10 - MovDelay[x][y]);
9755
9756       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9757     }
9758   }
9759 }
9760
9761 void MauerWaechst(int x, int y)
9762 {
9763   int delay = 6;
9764
9765   if (!MovDelay[x][y])          /* next animation frame */
9766     MovDelay[x][y] = 3 * delay;
9767
9768   if (MovDelay[x][y])           /* wait some time before next frame */
9769   {
9770     MovDelay[x][y]--;
9771
9772     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9773     {
9774       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9775       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9776
9777       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9778     }
9779
9780     if (!MovDelay[x][y])
9781     {
9782       if (MovDir[x][y] == MV_LEFT)
9783       {
9784         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9785           TEST_DrawLevelField(x - 1, y);
9786       }
9787       else if (MovDir[x][y] == MV_RIGHT)
9788       {
9789         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9790           TEST_DrawLevelField(x + 1, y);
9791       }
9792       else if (MovDir[x][y] == MV_UP)
9793       {
9794         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9795           TEST_DrawLevelField(x, y - 1);
9796       }
9797       else
9798       {
9799         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9800           TEST_DrawLevelField(x, y + 1);
9801       }
9802
9803       Feld[x][y] = Store[x][y];
9804       Store[x][y] = 0;
9805       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9806       TEST_DrawLevelField(x, y);
9807     }
9808   }
9809 }
9810
9811 void MauerAbleger(int ax, int ay)
9812 {
9813   int element = Feld[ax][ay];
9814   int graphic = el2img(element);
9815   boolean oben_frei = FALSE, unten_frei = FALSE;
9816   boolean links_frei = FALSE, rechts_frei = FALSE;
9817   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9818   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9819   boolean new_wall = FALSE;
9820
9821   if (IS_ANIMATED(graphic))
9822     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9823
9824   if (!MovDelay[ax][ay])        /* start building new wall */
9825     MovDelay[ax][ay] = 6;
9826
9827   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9828   {
9829     MovDelay[ax][ay]--;
9830     if (MovDelay[ax][ay])
9831       return;
9832   }
9833
9834   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9835     oben_frei = TRUE;
9836   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9837     unten_frei = TRUE;
9838   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9839     links_frei = TRUE;
9840   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9841     rechts_frei = TRUE;
9842
9843   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9844       element == EL_EXPANDABLE_WALL_ANY)
9845   {
9846     if (oben_frei)
9847     {
9848       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9849       Store[ax][ay-1] = element;
9850       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9851       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9852         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9853                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9854       new_wall = TRUE;
9855     }
9856     if (unten_frei)
9857     {
9858       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9859       Store[ax][ay+1] = element;
9860       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9861       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9862         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9863                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9864       new_wall = TRUE;
9865     }
9866   }
9867
9868   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9869       element == EL_EXPANDABLE_WALL_ANY ||
9870       element == EL_EXPANDABLE_WALL ||
9871       element == EL_BD_EXPANDABLE_WALL)
9872   {
9873     if (links_frei)
9874     {
9875       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9876       Store[ax-1][ay] = element;
9877       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9878       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9879         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9880                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9881       new_wall = TRUE;
9882     }
9883
9884     if (rechts_frei)
9885     {
9886       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9887       Store[ax+1][ay] = element;
9888       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9889       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9890         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9891                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9892       new_wall = TRUE;
9893     }
9894   }
9895
9896   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9897     TEST_DrawLevelField(ax, ay);
9898
9899   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9900     oben_massiv = TRUE;
9901   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9902     unten_massiv = TRUE;
9903   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9904     links_massiv = TRUE;
9905   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9906     rechts_massiv = TRUE;
9907
9908   if (((oben_massiv && unten_massiv) ||
9909        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9910        element == EL_EXPANDABLE_WALL) &&
9911       ((links_massiv && rechts_massiv) ||
9912        element == EL_EXPANDABLE_WALL_VERTICAL))
9913     Feld[ax][ay] = EL_WALL;
9914
9915   if (new_wall)
9916     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9917 }
9918
9919 void MauerAblegerStahl(int ax, int ay)
9920 {
9921   int element = Feld[ax][ay];
9922   int graphic = el2img(element);
9923   boolean oben_frei = FALSE, unten_frei = FALSE;
9924   boolean links_frei = FALSE, rechts_frei = FALSE;
9925   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9926   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9927   boolean new_wall = FALSE;
9928
9929   if (IS_ANIMATED(graphic))
9930     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9931
9932   if (!MovDelay[ax][ay])        /* start building new wall */
9933     MovDelay[ax][ay] = 6;
9934
9935   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9936   {
9937     MovDelay[ax][ay]--;
9938     if (MovDelay[ax][ay])
9939       return;
9940   }
9941
9942   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9943     oben_frei = TRUE;
9944   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9945     unten_frei = TRUE;
9946   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9947     links_frei = TRUE;
9948   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9949     rechts_frei = TRUE;
9950
9951   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9952       element == EL_EXPANDABLE_STEELWALL_ANY)
9953   {
9954     if (oben_frei)
9955     {
9956       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9957       Store[ax][ay-1] = element;
9958       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9959       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9960         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9961                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9962       new_wall = TRUE;
9963     }
9964     if (unten_frei)
9965     {
9966       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9967       Store[ax][ay+1] = element;
9968       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9969       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9970         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9971                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9972       new_wall = TRUE;
9973     }
9974   }
9975
9976   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9977       element == EL_EXPANDABLE_STEELWALL_ANY)
9978   {
9979     if (links_frei)
9980     {
9981       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9982       Store[ax-1][ay] = element;
9983       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9984       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9985         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9986                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9987       new_wall = TRUE;
9988     }
9989
9990     if (rechts_frei)
9991     {
9992       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9993       Store[ax+1][ay] = element;
9994       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9995       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9996         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9997                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9998       new_wall = TRUE;
9999     }
10000   }
10001
10002   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10003     oben_massiv = TRUE;
10004   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10005     unten_massiv = TRUE;
10006   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10007     links_massiv = TRUE;
10008   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10009     rechts_massiv = TRUE;
10010
10011   if (((oben_massiv && unten_massiv) ||
10012        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10013       ((links_massiv && rechts_massiv) ||
10014        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10015     Feld[ax][ay] = EL_STEELWALL;
10016
10017   if (new_wall)
10018     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10019 }
10020
10021 void CheckForDragon(int x, int y)
10022 {
10023   int i, j;
10024   boolean dragon_found = FALSE;
10025   static int xy[4][2] =
10026   {
10027     { 0, -1 },
10028     { -1, 0 },
10029     { +1, 0 },
10030     { 0, +1 }
10031   };
10032
10033   for (i = 0; i < NUM_DIRECTIONS; i++)
10034   {
10035     for (j = 0; j < 4; j++)
10036     {
10037       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10038
10039       if (IN_LEV_FIELD(xx, yy) &&
10040           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10041       {
10042         if (Feld[xx][yy] == EL_DRAGON)
10043           dragon_found = TRUE;
10044       }
10045       else
10046         break;
10047     }
10048   }
10049
10050   if (!dragon_found)
10051   {
10052     for (i = 0; i < NUM_DIRECTIONS; i++)
10053     {
10054       for (j = 0; j < 3; j++)
10055       {
10056         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10057   
10058         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10059         {
10060           Feld[xx][yy] = EL_EMPTY;
10061           TEST_DrawLevelField(xx, yy);
10062         }
10063         else
10064           break;
10065       }
10066     }
10067   }
10068 }
10069
10070 static void InitBuggyBase(int x, int y)
10071 {
10072   int element = Feld[x][y];
10073   int activating_delay = FRAMES_PER_SECOND / 4;
10074
10075   ChangeDelay[x][y] =
10076     (element == EL_SP_BUGGY_BASE ?
10077      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10078      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10079      activating_delay :
10080      element == EL_SP_BUGGY_BASE_ACTIVE ?
10081      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10082 }
10083
10084 static void WarnBuggyBase(int x, int y)
10085 {
10086   int i;
10087   static int xy[4][2] =
10088   {
10089     { 0, -1 },
10090     { -1, 0 },
10091     { +1, 0 },
10092     { 0, +1 }
10093   };
10094
10095   for (i = 0; i < NUM_DIRECTIONS; i++)
10096   {
10097     int xx = x + xy[i][0];
10098     int yy = y + xy[i][1];
10099
10100     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10101     {
10102       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10103
10104       break;
10105     }
10106   }
10107 }
10108
10109 static void InitTrap(int x, int y)
10110 {
10111   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10112 }
10113
10114 static void ActivateTrap(int x, int y)
10115 {
10116   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10117 }
10118
10119 static void ChangeActiveTrap(int x, int y)
10120 {
10121   int graphic = IMG_TRAP_ACTIVE;
10122
10123   /* if new animation frame was drawn, correct crumbled sand border */
10124   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10125     TEST_DrawLevelFieldCrumbledSand(x, y);
10126 }
10127
10128 static int getSpecialActionElement(int element, int number, int base_element)
10129 {
10130   return (element != EL_EMPTY ? element :
10131           number != -1 ? base_element + number - 1 :
10132           EL_EMPTY);
10133 }
10134
10135 static int getModifiedActionNumber(int value_old, int operator, int operand,
10136                                    int value_min, int value_max)
10137 {
10138   int value_new = (operator == CA_MODE_SET      ? operand :
10139                    operator == CA_MODE_ADD      ? value_old + operand :
10140                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10141                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10142                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10143                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10144                    value_old);
10145
10146   return (value_new < value_min ? value_min :
10147           value_new > value_max ? value_max :
10148           value_new);
10149 }
10150
10151 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10152 {
10153   struct ElementInfo *ei = &element_info[element];
10154   struct ElementChangeInfo *change = &ei->change_page[page];
10155   int target_element = change->target_element;
10156   int action_type = change->action_type;
10157   int action_mode = change->action_mode;
10158   int action_arg = change->action_arg;
10159   int action_element = change->action_element;
10160   int i;
10161
10162   if (!change->has_action)
10163     return;
10164
10165   /* ---------- determine action paramater values -------------------------- */
10166
10167   int level_time_value =
10168     (level.time > 0 ? TimeLeft :
10169      TimePlayed);
10170
10171   int action_arg_element_raw =
10172     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10173      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10174      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10175      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10176      EL_EMPTY);
10177   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10178
10179 #if 0
10180   if (action_arg_element_raw == EL_GROUP_START)
10181     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10182 #endif
10183
10184   int action_arg_direction =
10185     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10186      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10187      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10188      change->actual_trigger_side :
10189      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10190      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10191      MV_NONE);
10192
10193   int action_arg_number_min =
10194     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10195      CA_ARG_MIN);
10196
10197   int action_arg_number_max =
10198     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10199      action_type == CA_SET_LEVEL_GEMS ? 999 :
10200      action_type == CA_SET_LEVEL_TIME ? 9999 :
10201      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10202      action_type == CA_SET_CE_VALUE ? 9999 :
10203      action_type == CA_SET_CE_SCORE ? 9999 :
10204      CA_ARG_MAX);
10205
10206   int action_arg_number_reset =
10207     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10208      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10209      action_type == CA_SET_LEVEL_TIME ? level.time :
10210      action_type == CA_SET_LEVEL_SCORE ? 0 :
10211 #if USE_NEW_CUSTOM_VALUE
10212      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10213 #else
10214      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10215 #endif
10216      action_type == CA_SET_CE_SCORE ? 0 :
10217      0);
10218
10219   int action_arg_number =
10220     (action_arg <= CA_ARG_MAX ? action_arg :
10221      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10222      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10223      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10224      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10225      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10226      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10227 #if USE_NEW_CUSTOM_VALUE
10228      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10229 #else
10230      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10231 #endif
10232      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10233      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10234      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10235      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10236      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10237      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10238      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10239      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10240      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10241      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10242      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10243      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10244      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10245      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10246      -1);
10247
10248   int action_arg_number_old =
10249     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10250      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10251      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10252      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10253      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10254      0);
10255
10256   int action_arg_number_new =
10257     getModifiedActionNumber(action_arg_number_old,
10258                             action_mode, action_arg_number,
10259                             action_arg_number_min, action_arg_number_max);
10260
10261 #if 1
10262   int trigger_player_bits = change->actual_trigger_player_bits;
10263 #else
10264   int trigger_player_bits =
10265     (change->actual_trigger_player >= EL_PLAYER_1 &&
10266      change->actual_trigger_player <= EL_PLAYER_4 ?
10267      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10268      PLAYER_BITS_ANY);
10269 #endif
10270
10271   int action_arg_player_bits =
10272     (action_arg >= CA_ARG_PLAYER_1 &&
10273      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10274      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10275      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10276      PLAYER_BITS_ANY);
10277
10278   /* ---------- execute action  -------------------------------------------- */
10279
10280   switch (action_type)
10281   {
10282     case CA_NO_ACTION:
10283     {
10284       return;
10285     }
10286
10287     /* ---------- level actions  ------------------------------------------- */
10288
10289     case CA_RESTART_LEVEL:
10290     {
10291       game.restart_level = TRUE;
10292
10293       break;
10294     }
10295
10296     case CA_SHOW_ENVELOPE:
10297     {
10298       int element = getSpecialActionElement(action_arg_element,
10299                                             action_arg_number, EL_ENVELOPE_1);
10300
10301       if (IS_ENVELOPE(element))
10302         local_player->show_envelope = element;
10303
10304       break;
10305     }
10306
10307     case CA_SET_LEVEL_TIME:
10308     {
10309       if (level.time > 0)       /* only modify limited time value */
10310       {
10311         TimeLeft = action_arg_number_new;
10312
10313 #if 1
10314         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10315
10316         DisplayGameControlValues();
10317 #else
10318         DrawGameValue_Time(TimeLeft);
10319 #endif
10320
10321         if (!TimeLeft && setup.time_limit)
10322           for (i = 0; i < MAX_PLAYERS; i++)
10323             KillPlayer(&stored_player[i]);
10324       }
10325
10326       break;
10327     }
10328
10329     case CA_SET_LEVEL_SCORE:
10330     {
10331       local_player->score = action_arg_number_new;
10332
10333 #if 1
10334       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10335
10336       DisplayGameControlValues();
10337 #else
10338       DrawGameValue_Score(local_player->score);
10339 #endif
10340
10341       break;
10342     }
10343
10344     case CA_SET_LEVEL_GEMS:
10345     {
10346       local_player->gems_still_needed = action_arg_number_new;
10347
10348 #if 1
10349       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10350
10351       DisplayGameControlValues();
10352 #else
10353       DrawGameValue_Emeralds(local_player->gems_still_needed);
10354 #endif
10355
10356       break;
10357     }
10358
10359 #if !USE_PLAYER_GRAVITY
10360     case CA_SET_LEVEL_GRAVITY:
10361     {
10362       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10363                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10364                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10365                       game.gravity);
10366       break;
10367     }
10368 #endif
10369
10370     case CA_SET_LEVEL_WIND:
10371     {
10372       game.wind_direction = action_arg_direction;
10373
10374       break;
10375     }
10376
10377     /* ---------- player actions  ------------------------------------------ */
10378
10379     case CA_MOVE_PLAYER:
10380     {
10381       /* automatically move to the next field in specified direction */
10382       for (i = 0; i < MAX_PLAYERS; i++)
10383         if (trigger_player_bits & (1 << i))
10384           stored_player[i].programmed_action = action_arg_direction;
10385
10386       break;
10387     }
10388
10389     case CA_EXIT_PLAYER:
10390     {
10391       for (i = 0; i < MAX_PLAYERS; i++)
10392         if (action_arg_player_bits & (1 << i))
10393           PlayerWins(&stored_player[i]);
10394
10395       break;
10396     }
10397
10398     case CA_KILL_PLAYER:
10399     {
10400       for (i = 0; i < MAX_PLAYERS; i++)
10401         if (action_arg_player_bits & (1 << i))
10402           KillPlayer(&stored_player[i]);
10403
10404       break;
10405     }
10406
10407     case CA_SET_PLAYER_KEYS:
10408     {
10409       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10410       int element = getSpecialActionElement(action_arg_element,
10411                                             action_arg_number, EL_KEY_1);
10412
10413       if (IS_KEY(element))
10414       {
10415         for (i = 0; i < MAX_PLAYERS; i++)
10416         {
10417           if (trigger_player_bits & (1 << i))
10418           {
10419             stored_player[i].key[KEY_NR(element)] = key_state;
10420
10421             DrawGameDoorValues();
10422           }
10423         }
10424       }
10425
10426       break;
10427     }
10428
10429     case CA_SET_PLAYER_SPEED:
10430     {
10431       for (i = 0; i < MAX_PLAYERS; i++)
10432       {
10433         if (trigger_player_bits & (1 << i))
10434         {
10435           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10436
10437           if (action_arg == CA_ARG_SPEED_FASTER &&
10438               stored_player[i].cannot_move)
10439           {
10440             action_arg_number = STEPSIZE_VERY_SLOW;
10441           }
10442           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10443                    action_arg == CA_ARG_SPEED_FASTER)
10444           {
10445             action_arg_number = 2;
10446             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10447                            CA_MODE_MULTIPLY);
10448           }
10449           else if (action_arg == CA_ARG_NUMBER_RESET)
10450           {
10451             action_arg_number = level.initial_player_stepsize[i];
10452           }
10453
10454           move_stepsize =
10455             getModifiedActionNumber(move_stepsize,
10456                                     action_mode,
10457                                     action_arg_number,
10458                                     action_arg_number_min,
10459                                     action_arg_number_max);
10460
10461           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10462         }
10463       }
10464
10465       break;
10466     }
10467
10468     case CA_SET_PLAYER_SHIELD:
10469     {
10470       for (i = 0; i < MAX_PLAYERS; i++)
10471       {
10472         if (trigger_player_bits & (1 << i))
10473         {
10474           if (action_arg == CA_ARG_SHIELD_OFF)
10475           {
10476             stored_player[i].shield_normal_time_left = 0;
10477             stored_player[i].shield_deadly_time_left = 0;
10478           }
10479           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10480           {
10481             stored_player[i].shield_normal_time_left = 999999;
10482           }
10483           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10484           {
10485             stored_player[i].shield_normal_time_left = 999999;
10486             stored_player[i].shield_deadly_time_left = 999999;
10487           }
10488         }
10489       }
10490
10491       break;
10492     }
10493
10494 #if USE_PLAYER_GRAVITY
10495     case CA_SET_PLAYER_GRAVITY:
10496     {
10497       for (i = 0; i < MAX_PLAYERS; i++)
10498       {
10499         if (trigger_player_bits & (1 << i))
10500         {
10501           stored_player[i].gravity =
10502             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10503              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10504              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10505              stored_player[i].gravity);
10506         }
10507       }
10508
10509       break;
10510     }
10511 #endif
10512
10513     case CA_SET_PLAYER_ARTWORK:
10514     {
10515       for (i = 0; i < MAX_PLAYERS; i++)
10516       {
10517         if (trigger_player_bits & (1 << i))
10518         {
10519           int artwork_element = action_arg_element;
10520
10521           if (action_arg == CA_ARG_ELEMENT_RESET)
10522             artwork_element =
10523               (level.use_artwork_element[i] ? level.artwork_element[i] :
10524                stored_player[i].element_nr);
10525
10526 #if USE_GFX_RESET_PLAYER_ARTWORK
10527           if (stored_player[i].artwork_element != artwork_element)
10528             stored_player[i].Frame = 0;
10529 #endif
10530
10531           stored_player[i].artwork_element = artwork_element;
10532
10533           SetPlayerWaiting(&stored_player[i], FALSE);
10534
10535           /* set number of special actions for bored and sleeping animation */
10536           stored_player[i].num_special_action_bored =
10537             get_num_special_action(artwork_element,
10538                                    ACTION_BORING_1, ACTION_BORING_LAST);
10539           stored_player[i].num_special_action_sleeping =
10540             get_num_special_action(artwork_element,
10541                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10542         }
10543       }
10544
10545       break;
10546     }
10547
10548     /* ---------- CE actions  ---------------------------------------------- */
10549
10550     case CA_SET_CE_VALUE:
10551     {
10552 #if USE_NEW_CUSTOM_VALUE
10553       int last_ce_value = CustomValue[x][y];
10554
10555       CustomValue[x][y] = action_arg_number_new;
10556
10557       if (CustomValue[x][y] != last_ce_value)
10558       {
10559         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10560         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10561
10562         if (CustomValue[x][y] == 0)
10563         {
10564           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10565           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10566         }
10567       }
10568 #endif
10569
10570       break;
10571     }
10572
10573     case CA_SET_CE_SCORE:
10574     {
10575 #if USE_NEW_CUSTOM_VALUE
10576       int last_ce_score = ei->collect_score;
10577
10578       ei->collect_score = action_arg_number_new;
10579
10580       if (ei->collect_score != last_ce_score)
10581       {
10582         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10583         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10584
10585         if (ei->collect_score == 0)
10586         {
10587           int xx, yy;
10588
10589           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10590           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10591
10592           /*
10593             This is a very special case that seems to be a mixture between
10594             CheckElementChange() and CheckTriggeredElementChange(): while
10595             the first one only affects single elements that are triggered
10596             directly, the second one affects multiple elements in the playfield
10597             that are triggered indirectly by another element. This is a third
10598             case: Changing the CE score always affects multiple identical CEs,
10599             so every affected CE must be checked, not only the single CE for
10600             which the CE score was changed in the first place (as every instance
10601             of that CE shares the same CE score, and therefore also can change)!
10602           */
10603           SCAN_PLAYFIELD(xx, yy)
10604           {
10605             if (Feld[xx][yy] == element)
10606               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10607                                  CE_SCORE_GETS_ZERO);
10608           }
10609         }
10610       }
10611 #endif
10612
10613       break;
10614     }
10615
10616     case CA_SET_CE_ARTWORK:
10617     {
10618       int artwork_element = action_arg_element;
10619       boolean reset_frame = FALSE;
10620       int xx, yy;
10621
10622       if (action_arg == CA_ARG_ELEMENT_RESET)
10623         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10624                            element);
10625
10626       if (ei->gfx_element != artwork_element)
10627         reset_frame = TRUE;
10628
10629       ei->gfx_element = artwork_element;
10630
10631       SCAN_PLAYFIELD(xx, yy)
10632       {
10633         if (Feld[xx][yy] == element)
10634         {
10635           if (reset_frame)
10636           {
10637             ResetGfxAnimation(xx, yy);
10638             ResetRandomAnimationValue(xx, yy);
10639           }
10640
10641           TEST_DrawLevelField(xx, yy);
10642         }
10643       }
10644
10645       break;
10646     }
10647
10648     /* ---------- engine actions  ------------------------------------------ */
10649
10650     case CA_SET_ENGINE_SCAN_MODE:
10651     {
10652       InitPlayfieldScanMode(action_arg);
10653
10654       break;
10655     }
10656
10657     default:
10658       break;
10659   }
10660 }
10661
10662 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10663 {
10664   int old_element = Feld[x][y];
10665   int new_element = GetElementFromGroupElement(element);
10666   int previous_move_direction = MovDir[x][y];
10667 #if USE_NEW_CUSTOM_VALUE
10668   int last_ce_value = CustomValue[x][y];
10669 #endif
10670   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10671   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10672   boolean add_player_onto_element = (new_element_is_player &&
10673 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10674                                      /* this breaks SnakeBite when a snake is
10675                                         halfway through a door that closes */
10676                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10677                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10678 #endif
10679                                      IS_WALKABLE(old_element));
10680
10681 #if 0
10682   /* check if element under the player changes from accessible to unaccessible
10683      (needed for special case of dropping element which then changes) */
10684   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10685       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10686   {
10687     Bang(x, y);
10688
10689     return;
10690   }
10691 #endif
10692
10693   if (!add_player_onto_element)
10694   {
10695     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10696       RemoveMovingField(x, y);
10697     else
10698       RemoveField(x, y);
10699
10700     Feld[x][y] = new_element;
10701
10702 #if !USE_GFX_RESET_GFX_ANIMATION
10703     ResetGfxAnimation(x, y);
10704     ResetRandomAnimationValue(x, y);
10705 #endif
10706
10707     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10708       MovDir[x][y] = previous_move_direction;
10709
10710 #if USE_NEW_CUSTOM_VALUE
10711     if (element_info[new_element].use_last_ce_value)
10712       CustomValue[x][y] = last_ce_value;
10713 #endif
10714
10715     InitField_WithBug1(x, y, FALSE);
10716
10717     new_element = Feld[x][y];   /* element may have changed */
10718
10719 #if USE_GFX_RESET_GFX_ANIMATION
10720     ResetGfxAnimation(x, y);
10721     ResetRandomAnimationValue(x, y);
10722 #endif
10723
10724     TEST_DrawLevelField(x, y);
10725
10726     if (GFX_CRUMBLED(new_element))
10727       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10728   }
10729
10730 #if 1
10731   /* check if element under the player changes from accessible to unaccessible
10732      (needed for special case of dropping element which then changes) */
10733   /* (must be checked after creating new element for walkable group elements) */
10734 #if USE_FIX_KILLED_BY_NON_WALKABLE
10735   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10736       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10737   {
10738     Bang(x, y);
10739
10740     return;
10741   }
10742 #else
10743   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10744       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10745   {
10746     Bang(x, y);
10747
10748     return;
10749   }
10750 #endif
10751 #endif
10752
10753   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10754   if (new_element_is_player)
10755     RelocatePlayer(x, y, new_element);
10756
10757   if (is_change)
10758     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10759
10760   TestIfBadThingTouchesPlayer(x, y);
10761   TestIfPlayerTouchesCustomElement(x, y);
10762   TestIfElementTouchesCustomElement(x, y);
10763 }
10764
10765 static void CreateField(int x, int y, int element)
10766 {
10767   CreateFieldExt(x, y, element, FALSE);
10768 }
10769
10770 static void CreateElementFromChange(int x, int y, int element)
10771 {
10772   element = GET_VALID_RUNTIME_ELEMENT(element);
10773
10774 #if USE_STOP_CHANGED_ELEMENTS
10775   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10776   {
10777     int old_element = Feld[x][y];
10778
10779     /* prevent changed element from moving in same engine frame
10780        unless both old and new element can either fall or move */
10781     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10782         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10783       Stop[x][y] = TRUE;
10784   }
10785 #endif
10786
10787   CreateFieldExt(x, y, element, TRUE);
10788 }
10789
10790 static boolean ChangeElement(int x, int y, int element, int page)
10791 {
10792   struct ElementInfo *ei = &element_info[element];
10793   struct ElementChangeInfo *change = &ei->change_page[page];
10794   int ce_value = CustomValue[x][y];
10795   int ce_score = ei->collect_score;
10796   int target_element;
10797   int old_element = Feld[x][y];
10798
10799   /* always use default change event to prevent running into a loop */
10800   if (ChangeEvent[x][y] == -1)
10801     ChangeEvent[x][y] = CE_DELAY;
10802
10803   if (ChangeEvent[x][y] == CE_DELAY)
10804   {
10805     /* reset actual trigger element, trigger player and action element */
10806     change->actual_trigger_element = EL_EMPTY;
10807     change->actual_trigger_player = EL_PLAYER_1;
10808     change->actual_trigger_player_bits = CH_PLAYER_1;
10809     change->actual_trigger_side = CH_SIDE_NONE;
10810     change->actual_trigger_ce_value = 0;
10811     change->actual_trigger_ce_score = 0;
10812   }
10813
10814   /* do not change elements more than a specified maximum number of changes */
10815   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10816     return FALSE;
10817
10818   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10819
10820   if (change->explode)
10821   {
10822     Bang(x, y);
10823
10824     return TRUE;
10825   }
10826
10827   if (change->use_target_content)
10828   {
10829     boolean complete_replace = TRUE;
10830     boolean can_replace[3][3];
10831     int xx, yy;
10832
10833     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10834     {
10835       boolean is_empty;
10836       boolean is_walkable;
10837       boolean is_diggable;
10838       boolean is_collectible;
10839       boolean is_removable;
10840       boolean is_destructible;
10841       int ex = x + xx - 1;
10842       int ey = y + yy - 1;
10843       int content_element = change->target_content.e[xx][yy];
10844       int e;
10845
10846       can_replace[xx][yy] = TRUE;
10847
10848       if (ex == x && ey == y)   /* do not check changing element itself */
10849         continue;
10850
10851       if (content_element == EL_EMPTY_SPACE)
10852       {
10853         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10854
10855         continue;
10856       }
10857
10858       if (!IN_LEV_FIELD(ex, ey))
10859       {
10860         can_replace[xx][yy] = FALSE;
10861         complete_replace = FALSE;
10862
10863         continue;
10864       }
10865
10866       e = Feld[ex][ey];
10867
10868       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10869         e = MovingOrBlocked2Element(ex, ey);
10870
10871       is_empty = (IS_FREE(ex, ey) ||
10872                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10873
10874       is_walkable     = (is_empty || IS_WALKABLE(e));
10875       is_diggable     = (is_empty || IS_DIGGABLE(e));
10876       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10877       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10878       is_removable    = (is_diggable || is_collectible);
10879
10880       can_replace[xx][yy] =
10881         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10882           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10883           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10884           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10885           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10886           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10887          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10888
10889       if (!can_replace[xx][yy])
10890         complete_replace = FALSE;
10891     }
10892
10893     if (!change->only_if_complete || complete_replace)
10894     {
10895       boolean something_has_changed = FALSE;
10896
10897       if (change->only_if_complete && change->use_random_replace &&
10898           RND(100) < change->random_percentage)
10899         return FALSE;
10900
10901       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10902       {
10903         int ex = x + xx - 1;
10904         int ey = y + yy - 1;
10905         int content_element;
10906
10907         if (can_replace[xx][yy] && (!change->use_random_replace ||
10908                                     RND(100) < change->random_percentage))
10909         {
10910           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10911             RemoveMovingField(ex, ey);
10912
10913           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10914
10915           content_element = change->target_content.e[xx][yy];
10916           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10917                                               ce_value, ce_score);
10918
10919           CreateElementFromChange(ex, ey, target_element);
10920
10921           something_has_changed = TRUE;
10922
10923           /* for symmetry reasons, freeze newly created border elements */
10924           if (ex != x || ey != y)
10925             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10926         }
10927       }
10928
10929       if (something_has_changed)
10930       {
10931         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10932         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10933       }
10934     }
10935   }
10936   else
10937   {
10938     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10939                                         ce_value, ce_score);
10940
10941     if (element == EL_DIAGONAL_GROWING ||
10942         element == EL_DIAGONAL_SHRINKING)
10943     {
10944       target_element = Store[x][y];
10945
10946       Store[x][y] = EL_EMPTY;
10947     }
10948
10949     CreateElementFromChange(x, y, target_element);
10950
10951     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10952     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10953   }
10954
10955   /* this uses direct change before indirect change */
10956   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10957
10958   return TRUE;
10959 }
10960
10961 #if USE_NEW_DELAYED_ACTION
10962
10963 static void HandleElementChange(int x, int y, int page)
10964 {
10965   int element = MovingOrBlocked2Element(x, y);
10966   struct ElementInfo *ei = &element_info[element];
10967   struct ElementChangeInfo *change = &ei->change_page[page];
10968
10969 #ifdef DEBUG
10970   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10971       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10972   {
10973     printf("\n\n");
10974     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10975            x, y, element, element_info[element].token_name);
10976     printf("HandleElementChange(): This should never happen!\n");
10977     printf("\n\n");
10978   }
10979 #endif
10980
10981   /* this can happen with classic bombs on walkable, changing elements */
10982   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10983   {
10984 #if 0
10985     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10986       ChangeDelay[x][y] = 0;
10987 #endif
10988
10989     return;
10990   }
10991
10992   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10993   {
10994     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10995
10996     if (change->can_change)
10997     {
10998 #if 1
10999       /* !!! not clear why graphic animation should be reset at all here !!! */
11000       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11001 #if USE_GFX_RESET_WHEN_NOT_MOVING
11002       /* when a custom element is about to change (for example by change delay),
11003          do not reset graphic animation when the custom element is moving */
11004       if (!IS_MOVING(x, y))
11005 #endif
11006       {
11007         ResetGfxAnimation(x, y);
11008         ResetRandomAnimationValue(x, y);
11009       }
11010 #endif
11011
11012       if (change->pre_change_function)
11013         change->pre_change_function(x, y);
11014     }
11015   }
11016
11017   ChangeDelay[x][y]--;
11018
11019   if (ChangeDelay[x][y] != 0)           /* continue element change */
11020   {
11021     if (change->can_change)
11022     {
11023       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11024
11025       if (IS_ANIMATED(graphic))
11026         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11027
11028       if (change->change_function)
11029         change->change_function(x, y);
11030     }
11031   }
11032   else                                  /* finish element change */
11033   {
11034     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11035     {
11036       page = ChangePage[x][y];
11037       ChangePage[x][y] = -1;
11038
11039       change = &ei->change_page[page];
11040     }
11041
11042     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11043     {
11044       ChangeDelay[x][y] = 1;            /* try change after next move step */
11045       ChangePage[x][y] = page;          /* remember page to use for change */
11046
11047       return;
11048     }
11049
11050     if (change->can_change)
11051     {
11052       if (ChangeElement(x, y, element, page))
11053       {
11054         if (change->post_change_function)
11055           change->post_change_function(x, y);
11056       }
11057     }
11058
11059     if (change->has_action)
11060       ExecuteCustomElementAction(x, y, element, page);
11061   }
11062 }
11063
11064 #else
11065
11066 static void HandleElementChange(int x, int y, int page)
11067 {
11068   int element = MovingOrBlocked2Element(x, y);
11069   struct ElementInfo *ei = &element_info[element];
11070   struct ElementChangeInfo *change = &ei->change_page[page];
11071
11072 #ifdef DEBUG
11073   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11074   {
11075     printf("\n\n");
11076     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11077            x, y, element, element_info[element].token_name);
11078     printf("HandleElementChange(): This should never happen!\n");
11079     printf("\n\n");
11080   }
11081 #endif
11082
11083   /* this can happen with classic bombs on walkable, changing elements */
11084   if (!CAN_CHANGE(element))
11085   {
11086 #if 0
11087     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11088       ChangeDelay[x][y] = 0;
11089 #endif
11090
11091     return;
11092   }
11093
11094   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11095   {
11096     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11097
11098     ResetGfxAnimation(x, y);
11099     ResetRandomAnimationValue(x, y);
11100
11101     if (change->pre_change_function)
11102       change->pre_change_function(x, y);
11103   }
11104
11105   ChangeDelay[x][y]--;
11106
11107   if (ChangeDelay[x][y] != 0)           /* continue element change */
11108   {
11109     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11110
11111     if (IS_ANIMATED(graphic))
11112       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11113
11114     if (change->change_function)
11115       change->change_function(x, y);
11116   }
11117   else                                  /* finish element change */
11118   {
11119     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11120     {
11121       page = ChangePage[x][y];
11122       ChangePage[x][y] = -1;
11123
11124       change = &ei->change_page[page];
11125     }
11126
11127     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11128     {
11129       ChangeDelay[x][y] = 1;            /* try change after next move step */
11130       ChangePage[x][y] = page;          /* remember page to use for change */
11131
11132       return;
11133     }
11134
11135     if (ChangeElement(x, y, element, page))
11136     {
11137       if (change->post_change_function)
11138         change->post_change_function(x, y);
11139     }
11140   }
11141 }
11142
11143 #endif
11144
11145 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11146                                               int trigger_element,
11147                                               int trigger_event,
11148                                               int trigger_player,
11149                                               int trigger_side,
11150                                               int trigger_page)
11151 {
11152   boolean change_done_any = FALSE;
11153   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11154   int i;
11155
11156   if (!(trigger_events[trigger_element][trigger_event]))
11157     return FALSE;
11158
11159 #if 0
11160   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11161          trigger_event, recursion_loop_depth, recursion_loop_detected,
11162          recursion_loop_element, EL_NAME(recursion_loop_element));
11163 #endif
11164
11165   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11166
11167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11168   {
11169     int element = EL_CUSTOM_START + i;
11170     boolean change_done = FALSE;
11171     int p;
11172
11173     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11174         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11175       continue;
11176
11177     for (p = 0; p < element_info[element].num_change_pages; p++)
11178     {
11179       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11180
11181       if (change->can_change_or_has_action &&
11182           change->has_event[trigger_event] &&
11183           change->trigger_side & trigger_side &&
11184           change->trigger_player & trigger_player &&
11185           change->trigger_page & trigger_page_bits &&
11186           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11187       {
11188         change->actual_trigger_element = trigger_element;
11189         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11190         change->actual_trigger_player_bits = trigger_player;
11191         change->actual_trigger_side = trigger_side;
11192         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11193         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11194
11195 #if 0
11196         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11197                element, EL_NAME(element), p);
11198 #endif
11199
11200         if ((change->can_change && !change_done) || change->has_action)
11201         {
11202           int x, y;
11203
11204           SCAN_PLAYFIELD(x, y)
11205           {
11206             if (Feld[x][y] == element)
11207             {
11208               if (change->can_change && !change_done)
11209               {
11210 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11211                 /* if element already changed in this frame, not only prevent
11212                    another element change (checked in ChangeElement()), but
11213                    also prevent additional element actions for this element */
11214
11215                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11216                     !level.use_action_after_change_bug)
11217                   continue;
11218 #endif
11219
11220 #if 0
11221                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11222                        element, EL_NAME(element), p);
11223 #endif
11224
11225                 ChangeDelay[x][y] = 1;
11226                 ChangeEvent[x][y] = trigger_event;
11227
11228                 HandleElementChange(x, y, p);
11229               }
11230 #if USE_NEW_DELAYED_ACTION
11231               else if (change->has_action)
11232               {
11233 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11234                 /* if element already changed in this frame, not only prevent
11235                    another element change (checked in ChangeElement()), but
11236                    also prevent additional element actions for this element */
11237
11238                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11239                     !level.use_action_after_change_bug)
11240                   continue;
11241 #endif
11242
11243
11244 #if 0
11245                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11246                        element, EL_NAME(element), p);
11247 #endif
11248
11249                 ExecuteCustomElementAction(x, y, element, p);
11250                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11251               }
11252 #else
11253               if (change->has_action)
11254               {
11255                 ExecuteCustomElementAction(x, y, element, p);
11256                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11257               }
11258 #endif
11259             }
11260           }
11261
11262           if (change->can_change)
11263           {
11264             change_done = TRUE;
11265             change_done_any = TRUE;
11266
11267 #if 0
11268             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11269                    element, EL_NAME(element), p);
11270 #endif
11271
11272           }
11273         }
11274       }
11275     }
11276   }
11277
11278   RECURSION_LOOP_DETECTION_END();
11279
11280   return change_done_any;
11281 }
11282
11283 static boolean CheckElementChangeExt(int x, int y,
11284                                      int element,
11285                                      int trigger_element,
11286                                      int trigger_event,
11287                                      int trigger_player,
11288                                      int trigger_side)
11289 {
11290   boolean change_done = FALSE;
11291   int p;
11292
11293   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11294       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11295     return FALSE;
11296
11297   if (Feld[x][y] == EL_BLOCKED)
11298   {
11299     Blocked2Moving(x, y, &x, &y);
11300     element = Feld[x][y];
11301   }
11302
11303 #if 0
11304   /* check if element has already changed */
11305   if (Feld[x][y] != element)
11306     return FALSE;
11307 #else
11308   /* check if element has already changed or is about to change after moving */
11309   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11310        Feld[x][y] != element) ||
11311
11312       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11313        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11314         ChangePage[x][y] != -1)))
11315     return FALSE;
11316 #endif
11317
11318 #if 0
11319   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11320          trigger_event, recursion_loop_depth, recursion_loop_detected,
11321          recursion_loop_element, EL_NAME(recursion_loop_element));
11322 #endif
11323
11324   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11325
11326   for (p = 0; p < element_info[element].num_change_pages; p++)
11327   {
11328     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11329
11330     /* check trigger element for all events where the element that is checked
11331        for changing interacts with a directly adjacent element -- this is
11332        different to element changes that affect other elements to change on the
11333        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11334     boolean check_trigger_element =
11335       (trigger_event == CE_TOUCHING_X ||
11336        trigger_event == CE_HITTING_X ||
11337        trigger_event == CE_HIT_BY_X ||
11338 #if 1
11339        /* this one was forgotten until 3.2.3 */
11340        trigger_event == CE_DIGGING_X);
11341 #endif
11342
11343     if (change->can_change_or_has_action &&
11344         change->has_event[trigger_event] &&
11345         change->trigger_side & trigger_side &&
11346         change->trigger_player & trigger_player &&
11347         (!check_trigger_element ||
11348          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11349     {
11350       change->actual_trigger_element = trigger_element;
11351       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11352       change->actual_trigger_player_bits = trigger_player;
11353       change->actual_trigger_side = trigger_side;
11354       change->actual_trigger_ce_value = CustomValue[x][y];
11355       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11356
11357       /* special case: trigger element not at (x,y) position for some events */
11358       if (check_trigger_element)
11359       {
11360         static struct
11361         {
11362           int dx, dy;
11363         } move_xy[] =
11364           {
11365             {  0,  0 },
11366             { -1,  0 },
11367             { +1,  0 },
11368             {  0,  0 },
11369             {  0, -1 },
11370             {  0,  0 }, { 0, 0 }, { 0, 0 },
11371             {  0, +1 }
11372           };
11373
11374         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11375         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11376
11377         change->actual_trigger_ce_value = CustomValue[xx][yy];
11378         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11379       }
11380
11381       if (change->can_change && !change_done)
11382       {
11383         ChangeDelay[x][y] = 1;
11384         ChangeEvent[x][y] = trigger_event;
11385
11386         HandleElementChange(x, y, p);
11387
11388         change_done = TRUE;
11389       }
11390 #if USE_NEW_DELAYED_ACTION
11391       else if (change->has_action)
11392       {
11393         ExecuteCustomElementAction(x, y, element, p);
11394         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11395       }
11396 #else
11397       if (change->has_action)
11398       {
11399         ExecuteCustomElementAction(x, y, element, p);
11400         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11401       }
11402 #endif
11403     }
11404   }
11405
11406   RECURSION_LOOP_DETECTION_END();
11407
11408   return change_done;
11409 }
11410
11411 static void PlayPlayerSound(struct PlayerInfo *player)
11412 {
11413   int jx = player->jx, jy = player->jy;
11414   int sound_element = player->artwork_element;
11415   int last_action = player->last_action_waiting;
11416   int action = player->action_waiting;
11417
11418   if (player->is_waiting)
11419   {
11420     if (action != last_action)
11421       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11422     else
11423       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11424   }
11425   else
11426   {
11427     if (action != last_action)
11428       StopSound(element_info[sound_element].sound[last_action]);
11429
11430     if (last_action == ACTION_SLEEPING)
11431       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11432   }
11433 }
11434
11435 static void PlayAllPlayersSound()
11436 {
11437   int i;
11438
11439   for (i = 0; i < MAX_PLAYERS; i++)
11440     if (stored_player[i].active)
11441       PlayPlayerSound(&stored_player[i]);
11442 }
11443
11444 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11445 {
11446   boolean last_waiting = player->is_waiting;
11447   int move_dir = player->MovDir;
11448
11449   player->dir_waiting = move_dir;
11450   player->last_action_waiting = player->action_waiting;
11451
11452   if (is_waiting)
11453   {
11454     if (!last_waiting)          /* not waiting -> waiting */
11455     {
11456       player->is_waiting = TRUE;
11457
11458       player->frame_counter_bored =
11459         FrameCounter +
11460         game.player_boring_delay_fixed +
11461         GetSimpleRandom(game.player_boring_delay_random);
11462       player->frame_counter_sleeping =
11463         FrameCounter +
11464         game.player_sleeping_delay_fixed +
11465         GetSimpleRandom(game.player_sleeping_delay_random);
11466
11467       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11468     }
11469
11470     if (game.player_sleeping_delay_fixed +
11471         game.player_sleeping_delay_random > 0 &&
11472         player->anim_delay_counter == 0 &&
11473         player->post_delay_counter == 0 &&
11474         FrameCounter >= player->frame_counter_sleeping)
11475       player->is_sleeping = TRUE;
11476     else if (game.player_boring_delay_fixed +
11477              game.player_boring_delay_random > 0 &&
11478              FrameCounter >= player->frame_counter_bored)
11479       player->is_bored = TRUE;
11480
11481     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11482                               player->is_bored ? ACTION_BORING :
11483                               ACTION_WAITING);
11484
11485     if (player->is_sleeping && player->use_murphy)
11486     {
11487       /* special case for sleeping Murphy when leaning against non-free tile */
11488
11489       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11490           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11491            !IS_MOVING(player->jx - 1, player->jy)))
11492         move_dir = MV_LEFT;
11493       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11494                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11495                 !IS_MOVING(player->jx + 1, player->jy)))
11496         move_dir = MV_RIGHT;
11497       else
11498         player->is_sleeping = FALSE;
11499
11500       player->dir_waiting = move_dir;
11501     }
11502
11503     if (player->is_sleeping)
11504     {
11505       if (player->num_special_action_sleeping > 0)
11506       {
11507         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11508         {
11509           int last_special_action = player->special_action_sleeping;
11510           int num_special_action = player->num_special_action_sleeping;
11511           int special_action =
11512             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11513              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11514              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11515              last_special_action + 1 : ACTION_SLEEPING);
11516           int special_graphic =
11517             el_act_dir2img(player->artwork_element, special_action, move_dir);
11518
11519           player->anim_delay_counter =
11520             graphic_info[special_graphic].anim_delay_fixed +
11521             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11522           player->post_delay_counter =
11523             graphic_info[special_graphic].post_delay_fixed +
11524             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11525
11526           player->special_action_sleeping = special_action;
11527         }
11528
11529         if (player->anim_delay_counter > 0)
11530         {
11531           player->action_waiting = player->special_action_sleeping;
11532           player->anim_delay_counter--;
11533         }
11534         else if (player->post_delay_counter > 0)
11535         {
11536           player->post_delay_counter--;
11537         }
11538       }
11539     }
11540     else if (player->is_bored)
11541     {
11542       if (player->num_special_action_bored > 0)
11543       {
11544         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11545         {
11546           int special_action =
11547             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11548           int special_graphic =
11549             el_act_dir2img(player->artwork_element, special_action, move_dir);
11550
11551           player->anim_delay_counter =
11552             graphic_info[special_graphic].anim_delay_fixed +
11553             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11554           player->post_delay_counter =
11555             graphic_info[special_graphic].post_delay_fixed +
11556             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11557
11558           player->special_action_bored = special_action;
11559         }
11560
11561         if (player->anim_delay_counter > 0)
11562         {
11563           player->action_waiting = player->special_action_bored;
11564           player->anim_delay_counter--;
11565         }
11566         else if (player->post_delay_counter > 0)
11567         {
11568           player->post_delay_counter--;
11569         }
11570       }
11571     }
11572   }
11573   else if (last_waiting)        /* waiting -> not waiting */
11574   {
11575     player->is_waiting = FALSE;
11576     player->is_bored = FALSE;
11577     player->is_sleeping = FALSE;
11578
11579     player->frame_counter_bored = -1;
11580     player->frame_counter_sleeping = -1;
11581
11582     player->anim_delay_counter = 0;
11583     player->post_delay_counter = 0;
11584
11585     player->dir_waiting = player->MovDir;
11586     player->action_waiting = ACTION_DEFAULT;
11587
11588     player->special_action_bored = ACTION_DEFAULT;
11589     player->special_action_sleeping = ACTION_DEFAULT;
11590   }
11591 }
11592
11593 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11594 {
11595   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11596   int left      = player_action & JOY_LEFT;
11597   int right     = player_action & JOY_RIGHT;
11598   int up        = player_action & JOY_UP;
11599   int down      = player_action & JOY_DOWN;
11600   int button1   = player_action & JOY_BUTTON_1;
11601   int button2   = player_action & JOY_BUTTON_2;
11602   int dx        = (left ? -1 : right ? 1 : 0);
11603   int dy        = (up   ? -1 : down  ? 1 : 0);
11604
11605   if (!player->active || tape.pausing)
11606     return 0;
11607
11608   if (player_action)
11609   {
11610     if (button1)
11611       snapped = SnapField(player, dx, dy);
11612     else
11613     {
11614       if (button2)
11615         dropped = DropElement(player);
11616
11617       moved = MovePlayer(player, dx, dy);
11618     }
11619
11620     if (tape.single_step && tape.recording && !tape.pausing)
11621     {
11622       if (button1 || (dropped && !moved))
11623       {
11624         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11625         SnapField(player, 0, 0);                /* stop snapping */
11626       }
11627     }
11628
11629     SetPlayerWaiting(player, FALSE);
11630
11631     return player_action;
11632   }
11633   else
11634   {
11635     /* no actions for this player (no input at player's configured device) */
11636
11637     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11638     SnapField(player, 0, 0);
11639     CheckGravityMovementWhenNotMoving(player);
11640
11641     if (player->MovPos == 0)
11642       SetPlayerWaiting(player, TRUE);
11643
11644     if (player->MovPos == 0)    /* needed for tape.playing */
11645       player->is_moving = FALSE;
11646
11647     player->is_dropping = FALSE;
11648     player->is_dropping_pressed = FALSE;
11649     player->drop_pressed_delay = 0;
11650
11651     return 0;
11652   }
11653 }
11654
11655 static void CheckLevelTime()
11656 {
11657   int i;
11658
11659   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11660   {
11661     if (level.native_em_level->lev->home == 0)  /* all players at home */
11662     {
11663       PlayerWins(local_player);
11664
11665       AllPlayersGone = TRUE;
11666
11667       level.native_em_level->lev->home = -1;
11668     }
11669
11670     if (level.native_em_level->ply[0]->alive == 0 &&
11671         level.native_em_level->ply[1]->alive == 0 &&
11672         level.native_em_level->ply[2]->alive == 0 &&
11673         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11674       AllPlayersGone = TRUE;
11675   }
11676
11677   if (TimeFrames >= FRAMES_PER_SECOND)
11678   {
11679     TimeFrames = 0;
11680     TapeTime++;
11681
11682     for (i = 0; i < MAX_PLAYERS; i++)
11683     {
11684       struct PlayerInfo *player = &stored_player[i];
11685
11686       if (SHIELD_ON(player))
11687       {
11688         player->shield_normal_time_left--;
11689
11690         if (player->shield_deadly_time_left > 0)
11691           player->shield_deadly_time_left--;
11692       }
11693     }
11694
11695     if (!local_player->LevelSolved && !level.use_step_counter)
11696     {
11697       TimePlayed++;
11698
11699       if (TimeLeft > 0)
11700       {
11701         TimeLeft--;
11702
11703         if (TimeLeft <= 10 && setup.time_limit)
11704           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11705
11706 #if 1
11707         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11708
11709         DisplayGameControlValues();
11710 #else
11711         DrawGameValue_Time(TimeLeft);
11712 #endif
11713
11714         if (!TimeLeft && setup.time_limit)
11715         {
11716           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11717             level.native_em_level->lev->killed_out_of_time = TRUE;
11718           else
11719             for (i = 0; i < MAX_PLAYERS; i++)
11720               KillPlayer(&stored_player[i]);
11721         }
11722       }
11723 #if 1
11724       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11725       {
11726         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11727
11728         DisplayGameControlValues();
11729       }
11730 #else
11731       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11732         DrawGameValue_Time(TimePlayed);
11733 #endif
11734
11735       level.native_em_level->lev->time =
11736         (level.time == 0 ? TimePlayed : TimeLeft);
11737     }
11738
11739     if (tape.recording || tape.playing)
11740       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11741   }
11742
11743 #if 1
11744   UpdateAndDisplayGameControlValues();
11745 #else
11746   UpdateGameDoorValues();
11747   DrawGameDoorValues();
11748 #endif
11749 }
11750
11751 void AdvanceFrameAndPlayerCounters(int player_nr)
11752 {
11753   int i;
11754
11755   /* advance frame counters (global frame counter and time frame counter) */
11756   FrameCounter++;
11757   TimeFrames++;
11758
11759   /* advance player counters (counters for move delay, move animation etc.) */
11760   for (i = 0; i < MAX_PLAYERS; i++)
11761   {
11762     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11763     int move_delay_value = stored_player[i].move_delay_value;
11764     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11765
11766     if (!advance_player_counters)       /* not all players may be affected */
11767       continue;
11768
11769 #if USE_NEW_PLAYER_ANIM
11770     if (move_frames == 0)       /* less than one move per game frame */
11771     {
11772       int stepsize = TILEX / move_delay_value;
11773       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11774       int count = (stored_player[i].is_moving ?
11775                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11776
11777       if (count % delay == 0)
11778         move_frames = 1;
11779     }
11780 #endif
11781
11782     stored_player[i].Frame += move_frames;
11783
11784     if (stored_player[i].MovPos != 0)
11785       stored_player[i].StepFrame += move_frames;
11786
11787     if (stored_player[i].move_delay > 0)
11788       stored_player[i].move_delay--;
11789
11790     /* due to bugs in previous versions, counter must count up, not down */
11791     if (stored_player[i].push_delay != -1)
11792       stored_player[i].push_delay++;
11793
11794     if (stored_player[i].drop_delay > 0)
11795       stored_player[i].drop_delay--;
11796
11797     if (stored_player[i].is_dropping_pressed)
11798       stored_player[i].drop_pressed_delay++;
11799   }
11800 }
11801
11802 void StartGameActions(boolean init_network_game, boolean record_tape,
11803                       long random_seed)
11804 {
11805   unsigned long new_random_seed = InitRND(random_seed);
11806
11807   if (record_tape)
11808     TapeStartRecording(new_random_seed);
11809
11810 #if defined(NETWORK_AVALIABLE)
11811   if (init_network_game)
11812   {
11813     SendToServer_StartPlaying();
11814
11815     return;
11816   }
11817 #endif
11818
11819   InitGame();
11820 }
11821
11822 void GameActions()
11823 {
11824   static unsigned long game_frame_delay = 0;
11825   unsigned long game_frame_delay_value;
11826   byte *recorded_player_action;
11827   byte summarized_player_action = 0;
11828   byte tape_action[MAX_PLAYERS];
11829   int i;
11830
11831   /* detect endless loops, caused by custom element programming */
11832   if (recursion_loop_detected && recursion_loop_depth == 0)
11833   {
11834     char *message = getStringCat3("Internal Error ! Element ",
11835                                   EL_NAME(recursion_loop_element),
11836                                   " caused endless loop ! Quit the game ?");
11837
11838     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11839           EL_NAME(recursion_loop_element));
11840
11841     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11842
11843     recursion_loop_detected = FALSE;    /* if game should be continued */
11844
11845     free(message);
11846
11847     return;
11848   }
11849
11850   if (game.restart_level)
11851     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11852
11853   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11854   {
11855     if (level.native_em_level->lev->home == 0)  /* all players at home */
11856     {
11857       PlayerWins(local_player);
11858
11859       AllPlayersGone = TRUE;
11860
11861       level.native_em_level->lev->home = -1;
11862     }
11863
11864     if (level.native_em_level->ply[0]->alive == 0 &&
11865         level.native_em_level->ply[1]->alive == 0 &&
11866         level.native_em_level->ply[2]->alive == 0 &&
11867         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11868       AllPlayersGone = TRUE;
11869   }
11870
11871   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11872     GameWon();
11873
11874   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11875     TapeStop();
11876
11877   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11878     return;
11879
11880   game_frame_delay_value =
11881     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11882
11883   if (tape.playing && tape.warp_forward && !tape.pausing)
11884     game_frame_delay_value = 0;
11885
11886   /* ---------- main game synchronization point ---------- */
11887
11888   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11889
11890   if (network_playing && !network_player_action_received)
11891   {
11892     /* try to get network player actions in time */
11893
11894 #if defined(NETWORK_AVALIABLE)
11895     /* last chance to get network player actions without main loop delay */
11896     HandleNetworking();
11897 #endif
11898
11899     /* game was quit by network peer */
11900     if (game_status != GAME_MODE_PLAYING)
11901       return;
11902
11903     if (!network_player_action_received)
11904       return;           /* failed to get network player actions in time */
11905
11906     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11907   }
11908
11909   if (tape.pausing)
11910     return;
11911
11912   /* at this point we know that we really continue executing the game */
11913
11914   network_player_action_received = FALSE;
11915
11916   /* when playing tape, read previously recorded player input from tape data */
11917   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11918
11919 #if 1
11920   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11921   if (tape.pausing)
11922     return;
11923 #endif
11924
11925   if (tape.set_centered_player)
11926   {
11927     game.centered_player_nr_next = tape.centered_player_nr_next;
11928     game.set_centered_player = TRUE;
11929   }
11930
11931   for (i = 0; i < MAX_PLAYERS; i++)
11932   {
11933     summarized_player_action |= stored_player[i].action;
11934
11935     if (!network_playing)
11936       stored_player[i].effective_action = stored_player[i].action;
11937   }
11938
11939 #if defined(NETWORK_AVALIABLE)
11940   if (network_playing)
11941     SendToServer_MovePlayer(summarized_player_action);
11942 #endif
11943
11944   if (!options.network && !setup.team_mode)
11945     local_player->effective_action = summarized_player_action;
11946
11947   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11948   {
11949     for (i = 0; i < MAX_PLAYERS; i++)
11950       stored_player[i].effective_action =
11951         (i == game.centered_player_nr ? summarized_player_action : 0);
11952   }
11953
11954   if (recorded_player_action != NULL)
11955     for (i = 0; i < MAX_PLAYERS; i++)
11956       stored_player[i].effective_action = recorded_player_action[i];
11957
11958   for (i = 0; i < MAX_PLAYERS; i++)
11959   {
11960     tape_action[i] = stored_player[i].effective_action;
11961
11962     /* (this can only happen in the R'n'D game engine) */
11963     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11964       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11965   }
11966
11967   /* only record actions from input devices, but not programmed actions */
11968   if (tape.recording)
11969     TapeRecordAction(tape_action);
11970
11971   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11972   {
11973     GameActions_EM_Main();
11974   }
11975   else
11976   {
11977     GameActions_RND();
11978   }
11979 }
11980
11981 void GameActions_EM_Main()
11982 {
11983   byte effective_action[MAX_PLAYERS];
11984   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11985   int i;
11986
11987   for (i = 0; i < MAX_PLAYERS; i++)
11988     effective_action[i] = stored_player[i].effective_action;
11989
11990   GameActions_EM(effective_action, warp_mode);
11991
11992   CheckLevelTime();
11993
11994   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11995 }
11996
11997 void GameActions_RND()
11998 {
11999   int magic_wall_x = 0, magic_wall_y = 0;
12000   int i, x, y, element, graphic;
12001
12002   InitPlayfieldScanModeVars();
12003
12004 #if USE_ONE_MORE_CHANGE_PER_FRAME
12005   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12006   {
12007     SCAN_PLAYFIELD(x, y)
12008     {
12009       ChangeCount[x][y] = 0;
12010       ChangeEvent[x][y] = -1;
12011     }
12012   }
12013 #endif
12014
12015   if (game.set_centered_player)
12016   {
12017     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12018
12019     /* switching to "all players" only possible if all players fit to screen */
12020     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12021     {
12022       game.centered_player_nr_next = game.centered_player_nr;
12023       game.set_centered_player = FALSE;
12024     }
12025
12026     /* do not switch focus to non-existing (or non-active) player */
12027     if (game.centered_player_nr_next >= 0 &&
12028         !stored_player[game.centered_player_nr_next].active)
12029     {
12030       game.centered_player_nr_next = game.centered_player_nr;
12031       game.set_centered_player = FALSE;
12032     }
12033   }
12034
12035   if (game.set_centered_player &&
12036       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12037   {
12038     int sx, sy;
12039
12040     if (game.centered_player_nr_next == -1)
12041     {
12042       setScreenCenteredToAllPlayers(&sx, &sy);
12043     }
12044     else
12045     {
12046       sx = stored_player[game.centered_player_nr_next].jx;
12047       sy = stored_player[game.centered_player_nr_next].jy;
12048     }
12049
12050     game.centered_player_nr = game.centered_player_nr_next;
12051     game.set_centered_player = FALSE;
12052
12053     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12054     DrawGameDoorValues();
12055   }
12056
12057   for (i = 0; i < MAX_PLAYERS; i++)
12058   {
12059     int actual_player_action = stored_player[i].effective_action;
12060
12061 #if 1
12062     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12063        - rnd_equinox_tetrachloride 048
12064        - rnd_equinox_tetrachloride_ii 096
12065        - rnd_emanuel_schmieg 002
12066        - doctor_sloan_ww 001, 020
12067     */
12068     if (stored_player[i].MovPos == 0)
12069       CheckGravityMovement(&stored_player[i]);
12070 #endif
12071
12072     /* overwrite programmed action with tape action */
12073     if (stored_player[i].programmed_action)
12074       actual_player_action = stored_player[i].programmed_action;
12075
12076     PlayerActions(&stored_player[i], actual_player_action);
12077
12078     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12079   }
12080
12081   ScrollScreen(NULL, SCROLL_GO_ON);
12082
12083   /* for backwards compatibility, the following code emulates a fixed bug that
12084      occured when pushing elements (causing elements that just made their last
12085      pushing step to already (if possible) make their first falling step in the
12086      same game frame, which is bad); this code is also needed to use the famous
12087      "spring push bug" which is used in older levels and might be wanted to be
12088      used also in newer levels, but in this case the buggy pushing code is only
12089      affecting the "spring" element and no other elements */
12090
12091   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12092   {
12093     for (i = 0; i < MAX_PLAYERS; i++)
12094     {
12095       struct PlayerInfo *player = &stored_player[i];
12096       int x = player->jx;
12097       int y = player->jy;
12098
12099       if (player->active && player->is_pushing && player->is_moving &&
12100           IS_MOVING(x, y) &&
12101           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12102            Feld[x][y] == EL_SPRING))
12103       {
12104         ContinueMoving(x, y);
12105
12106         /* continue moving after pushing (this is actually a bug) */
12107         if (!IS_MOVING(x, y))
12108           Stop[x][y] = FALSE;
12109       }
12110     }
12111   }
12112
12113 #if 0
12114   debug_print_timestamp(0, "start main loop profiling");
12115 #endif
12116
12117   SCAN_PLAYFIELD(x, y)
12118   {
12119     ChangeCount[x][y] = 0;
12120     ChangeEvent[x][y] = -1;
12121
12122     /* this must be handled before main playfield loop */
12123     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12124     {
12125       MovDelay[x][y]--;
12126       if (MovDelay[x][y] <= 0)
12127         RemoveField(x, y);
12128     }
12129
12130 #if USE_NEW_SNAP_DELAY
12131     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12132     {
12133       MovDelay[x][y]--;
12134       if (MovDelay[x][y] <= 0)
12135       {
12136         RemoveField(x, y);
12137         TEST_DrawLevelField(x, y);
12138
12139         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12140       }
12141     }
12142 #endif
12143
12144 #if DEBUG
12145     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12146     {
12147       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12148       printf("GameActions(): This should never happen!\n");
12149
12150       ChangePage[x][y] = -1;
12151     }
12152 #endif
12153
12154     Stop[x][y] = FALSE;
12155     if (WasJustMoving[x][y] > 0)
12156       WasJustMoving[x][y]--;
12157     if (WasJustFalling[x][y] > 0)
12158       WasJustFalling[x][y]--;
12159     if (CheckCollision[x][y] > 0)
12160       CheckCollision[x][y]--;
12161     if (CheckImpact[x][y] > 0)
12162       CheckImpact[x][y]--;
12163
12164     GfxFrame[x][y]++;
12165
12166     /* reset finished pushing action (not done in ContinueMoving() to allow
12167        continuous pushing animation for elements with zero push delay) */
12168     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12169     {
12170       ResetGfxAnimation(x, y);
12171       TEST_DrawLevelField(x, y);
12172     }
12173
12174 #if DEBUG
12175     if (IS_BLOCKED(x, y))
12176     {
12177       int oldx, oldy;
12178
12179       Blocked2Moving(x, y, &oldx, &oldy);
12180       if (!IS_MOVING(oldx, oldy))
12181       {
12182         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12183         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12184         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12185         printf("GameActions(): This should never happen!\n");
12186       }
12187     }
12188 #endif
12189   }
12190
12191 #if 0
12192   debug_print_timestamp(0, "- time for pre-main loop:");
12193 #endif
12194
12195 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12196   SCAN_PLAYFIELD(x, y)
12197   {
12198     element = Feld[x][y];
12199     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12200
12201 #if 1
12202     {
12203 #if 1
12204       int element2 = element;
12205       int graphic2 = graphic;
12206 #else
12207       int element2 = Feld[x][y];
12208       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12209 #endif
12210       int last_gfx_frame = GfxFrame[x][y];
12211
12212       if (graphic_info[graphic2].anim_global_sync)
12213         GfxFrame[x][y] = FrameCounter;
12214       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12215         GfxFrame[x][y] = CustomValue[x][y];
12216       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12217         GfxFrame[x][y] = element_info[element2].collect_score;
12218       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12219         GfxFrame[x][y] = ChangeDelay[x][y];
12220
12221       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12222         DrawLevelGraphicAnimation(x, y, graphic2);
12223     }
12224 #else
12225     ResetGfxFrame(x, y, TRUE);
12226 #endif
12227
12228 #if 1
12229     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12230         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12231       ResetRandomAnimationValue(x, y);
12232 #endif
12233
12234 #if 1
12235     SetRandomAnimationValue(x, y);
12236 #endif
12237
12238 #if 1
12239     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12240 #endif
12241   }
12242 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12243
12244 #if 0
12245   debug_print_timestamp(0, "- time for TEST loop:     -->");
12246 #endif
12247
12248   SCAN_PLAYFIELD(x, y)
12249   {
12250     element = Feld[x][y];
12251     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12252
12253     ResetGfxFrame(x, y, TRUE);
12254
12255     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12256         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12257       ResetRandomAnimationValue(x, y);
12258
12259     SetRandomAnimationValue(x, y);
12260
12261     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12262
12263     if (IS_INACTIVE(element))
12264     {
12265       if (IS_ANIMATED(graphic))
12266         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12267
12268       continue;
12269     }
12270
12271     /* this may take place after moving, so 'element' may have changed */
12272     if (IS_CHANGING(x, y) &&
12273         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12274     {
12275       int page = element_info[element].event_page_nr[CE_DELAY];
12276
12277 #if 1
12278       HandleElementChange(x, y, page);
12279 #else
12280       if (CAN_CHANGE(element))
12281         HandleElementChange(x, y, page);
12282
12283       if (HAS_ACTION(element))
12284         ExecuteCustomElementAction(x, y, element, page);
12285 #endif
12286
12287       element = Feld[x][y];
12288       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12289     }
12290
12291 #if 0   // ---------------------------------------------------------------------
12292
12293     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12294     {
12295       StartMoving(x, y);
12296
12297       element = Feld[x][y];
12298       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12299
12300       if (IS_ANIMATED(graphic) &&
12301           !IS_MOVING(x, y) &&
12302           !Stop[x][y])
12303         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12304
12305       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12306         TEST_DrawTwinkleOnField(x, y);
12307     }
12308     else if (IS_MOVING(x, y))
12309       ContinueMoving(x, y);
12310     else
12311     {
12312       switch (element)
12313       {
12314         case EL_ACID:
12315         case EL_EXIT_OPEN:
12316         case EL_EM_EXIT_OPEN:
12317         case EL_SP_EXIT_OPEN:
12318         case EL_STEEL_EXIT_OPEN:
12319         case EL_EM_STEEL_EXIT_OPEN:
12320         case EL_SP_TERMINAL:
12321         case EL_SP_TERMINAL_ACTIVE:
12322         case EL_EXTRA_TIME:
12323         case EL_SHIELD_NORMAL:
12324         case EL_SHIELD_DEADLY:
12325           if (IS_ANIMATED(graphic))
12326             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12327           break;
12328
12329         case EL_DYNAMITE_ACTIVE:
12330         case EL_EM_DYNAMITE_ACTIVE:
12331         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12332         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12333         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12334         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12335         case EL_SP_DISK_RED_ACTIVE:
12336           CheckDynamite(x, y);
12337           break;
12338
12339         case EL_AMOEBA_GROWING:
12340           AmoebeWaechst(x, y);
12341           break;
12342
12343         case EL_AMOEBA_SHRINKING:
12344           AmoebaDisappearing(x, y);
12345           break;
12346
12347 #if !USE_NEW_AMOEBA_CODE
12348         case EL_AMOEBA_WET:
12349         case EL_AMOEBA_DRY:
12350         case EL_AMOEBA_FULL:
12351         case EL_BD_AMOEBA:
12352         case EL_EMC_DRIPPER:
12353           AmoebeAbleger(x, y);
12354           break;
12355 #endif
12356
12357         case EL_GAME_OF_LIFE:
12358         case EL_BIOMAZE:
12359           Life(x, y);
12360           break;
12361
12362         case EL_EXIT_CLOSED:
12363           CheckExit(x, y);
12364           break;
12365
12366         case EL_EM_EXIT_CLOSED:
12367           CheckExitEM(x, y);
12368           break;
12369
12370         case EL_STEEL_EXIT_CLOSED:
12371           CheckExitSteel(x, y);
12372           break;
12373
12374         case EL_EM_STEEL_EXIT_CLOSED:
12375           CheckExitSteelEM(x, y);
12376           break;
12377
12378         case EL_SP_EXIT_CLOSED:
12379           CheckExitSP(x, y);
12380           break;
12381
12382         case EL_EXPANDABLE_WALL_GROWING:
12383         case EL_EXPANDABLE_STEELWALL_GROWING:
12384           MauerWaechst(x, y);
12385           break;
12386
12387         case EL_EXPANDABLE_WALL:
12388         case EL_EXPANDABLE_WALL_HORIZONTAL:
12389         case EL_EXPANDABLE_WALL_VERTICAL:
12390         case EL_EXPANDABLE_WALL_ANY:
12391         case EL_BD_EXPANDABLE_WALL:
12392           MauerAbleger(x, y);
12393           break;
12394
12395         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12396         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12397         case EL_EXPANDABLE_STEELWALL_ANY:
12398           MauerAblegerStahl(x, y);
12399           break;
12400
12401         case EL_FLAMES:
12402           CheckForDragon(x, y);
12403           break;
12404
12405         case EL_EXPLOSION:
12406           break;
12407
12408         case EL_ELEMENT_SNAPPING:
12409         case EL_DIAGONAL_SHRINKING:
12410         case EL_DIAGONAL_GROWING:
12411         {
12412           graphic =
12413             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12414
12415           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12416           break;
12417         }
12418
12419         default:
12420           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12421             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12422           break;
12423       }
12424     }
12425
12426 #else   // ---------------------------------------------------------------------
12427
12428     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12429     {
12430       StartMoving(x, y);
12431
12432       element = Feld[x][y];
12433       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12434
12435       if (IS_ANIMATED(graphic) &&
12436           !IS_MOVING(x, y) &&
12437           !Stop[x][y])
12438         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12439
12440       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12441         TEST_DrawTwinkleOnField(x, y);
12442     }
12443     else if ((element == EL_ACID ||
12444               element == EL_EXIT_OPEN ||
12445               element == EL_EM_EXIT_OPEN ||
12446               element == EL_SP_EXIT_OPEN ||
12447               element == EL_STEEL_EXIT_OPEN ||
12448               element == EL_EM_STEEL_EXIT_OPEN ||
12449               element == EL_SP_TERMINAL ||
12450               element == EL_SP_TERMINAL_ACTIVE ||
12451               element == EL_EXTRA_TIME ||
12452               element == EL_SHIELD_NORMAL ||
12453               element == EL_SHIELD_DEADLY) &&
12454              IS_ANIMATED(graphic))
12455       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12456     else if (IS_MOVING(x, y))
12457       ContinueMoving(x, y);
12458     else if (IS_ACTIVE_BOMB(element))
12459       CheckDynamite(x, y);
12460     else if (element == EL_AMOEBA_GROWING)
12461       AmoebeWaechst(x, y);
12462     else if (element == EL_AMOEBA_SHRINKING)
12463       AmoebaDisappearing(x, y);
12464
12465 #if !USE_NEW_AMOEBA_CODE
12466     else if (IS_AMOEBALIVE(element))
12467       AmoebeAbleger(x, y);
12468 #endif
12469
12470     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12471       Life(x, y);
12472     else if (element == EL_EXIT_CLOSED)
12473       CheckExit(x, y);
12474     else if (element == EL_EM_EXIT_CLOSED)
12475       CheckExitEM(x, y);
12476     else if (element == EL_STEEL_EXIT_CLOSED)
12477       CheckExitSteel(x, y);
12478     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12479       CheckExitSteelEM(x, y);
12480     else if (element == EL_SP_EXIT_CLOSED)
12481       CheckExitSP(x, y);
12482     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12483              element == EL_EXPANDABLE_STEELWALL_GROWING)
12484       MauerWaechst(x, y);
12485     else if (element == EL_EXPANDABLE_WALL ||
12486              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12487              element == EL_EXPANDABLE_WALL_VERTICAL ||
12488              element == EL_EXPANDABLE_WALL_ANY ||
12489              element == EL_BD_EXPANDABLE_WALL)
12490       MauerAbleger(x, y);
12491     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12492              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12493              element == EL_EXPANDABLE_STEELWALL_ANY)
12494       MauerAblegerStahl(x, y);
12495     else if (element == EL_FLAMES)
12496       CheckForDragon(x, y);
12497     else if (element == EL_EXPLOSION)
12498       ; /* drawing of correct explosion animation is handled separately */
12499     else if (element == EL_ELEMENT_SNAPPING ||
12500              element == EL_DIAGONAL_SHRINKING ||
12501              element == EL_DIAGONAL_GROWING)
12502     {
12503       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12504
12505       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12506     }
12507     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12508       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12509
12510 #endif  // ---------------------------------------------------------------------
12511
12512     if (IS_BELT_ACTIVE(element))
12513       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12514
12515     if (game.magic_wall_active)
12516     {
12517       int jx = local_player->jx, jy = local_player->jy;
12518
12519       /* play the element sound at the position nearest to the player */
12520       if ((element == EL_MAGIC_WALL_FULL ||
12521            element == EL_MAGIC_WALL_ACTIVE ||
12522            element == EL_MAGIC_WALL_EMPTYING ||
12523            element == EL_BD_MAGIC_WALL_FULL ||
12524            element == EL_BD_MAGIC_WALL_ACTIVE ||
12525            element == EL_BD_MAGIC_WALL_EMPTYING ||
12526            element == EL_DC_MAGIC_WALL_FULL ||
12527            element == EL_DC_MAGIC_WALL_ACTIVE ||
12528            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12529           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12530       {
12531         magic_wall_x = x;
12532         magic_wall_y = y;
12533       }
12534     }
12535   }
12536
12537 #if 0
12538   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12539 #endif
12540
12541 #if USE_NEW_AMOEBA_CODE
12542   /* new experimental amoeba growth stuff */
12543   if (!(FrameCounter % 8))
12544   {
12545     static unsigned long random = 1684108901;
12546
12547     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12548     {
12549       x = RND(lev_fieldx);
12550       y = RND(lev_fieldy);
12551       element = Feld[x][y];
12552
12553       if (!IS_PLAYER(x,y) &&
12554           (element == EL_EMPTY ||
12555            CAN_GROW_INTO(element) ||
12556            element == EL_QUICKSAND_EMPTY ||
12557            element == EL_QUICKSAND_FAST_EMPTY ||
12558            element == EL_ACID_SPLASH_LEFT ||
12559            element == EL_ACID_SPLASH_RIGHT))
12560       {
12561         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12562             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12563             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12564             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12565           Feld[x][y] = EL_AMOEBA_DROP;
12566       }
12567
12568       random = random * 129 + 1;
12569     }
12570   }
12571 #endif
12572
12573 #if 0
12574   if (game.explosions_delayed)
12575 #endif
12576   {
12577     game.explosions_delayed = FALSE;
12578
12579     SCAN_PLAYFIELD(x, y)
12580     {
12581       element = Feld[x][y];
12582
12583       if (ExplodeField[x][y])
12584         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12585       else if (element == EL_EXPLOSION)
12586         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12587
12588       ExplodeField[x][y] = EX_TYPE_NONE;
12589     }
12590
12591     game.explosions_delayed = TRUE;
12592   }
12593
12594   if (game.magic_wall_active)
12595   {
12596     if (!(game.magic_wall_time_left % 4))
12597     {
12598       int element = Feld[magic_wall_x][magic_wall_y];
12599
12600       if (element == EL_BD_MAGIC_WALL_FULL ||
12601           element == EL_BD_MAGIC_WALL_ACTIVE ||
12602           element == EL_BD_MAGIC_WALL_EMPTYING)
12603         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12604       else if (element == EL_DC_MAGIC_WALL_FULL ||
12605                element == EL_DC_MAGIC_WALL_ACTIVE ||
12606                element == EL_DC_MAGIC_WALL_EMPTYING)
12607         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12608       else
12609         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12610     }
12611
12612     if (game.magic_wall_time_left > 0)
12613     {
12614       game.magic_wall_time_left--;
12615
12616       if (!game.magic_wall_time_left)
12617       {
12618         SCAN_PLAYFIELD(x, y)
12619         {
12620           element = Feld[x][y];
12621
12622           if (element == EL_MAGIC_WALL_ACTIVE ||
12623               element == EL_MAGIC_WALL_FULL)
12624           {
12625             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12626             TEST_DrawLevelField(x, y);
12627           }
12628           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12629                    element == EL_BD_MAGIC_WALL_FULL)
12630           {
12631             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12632             TEST_DrawLevelField(x, y);
12633           }
12634           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12635                    element == EL_DC_MAGIC_WALL_FULL)
12636           {
12637             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12638             TEST_DrawLevelField(x, y);
12639           }
12640         }
12641
12642         game.magic_wall_active = FALSE;
12643       }
12644     }
12645   }
12646
12647   if (game.light_time_left > 0)
12648   {
12649     game.light_time_left--;
12650
12651     if (game.light_time_left == 0)
12652       RedrawAllLightSwitchesAndInvisibleElements();
12653   }
12654
12655   if (game.timegate_time_left > 0)
12656   {
12657     game.timegate_time_left--;
12658
12659     if (game.timegate_time_left == 0)
12660       CloseAllOpenTimegates();
12661   }
12662
12663   if (game.lenses_time_left > 0)
12664   {
12665     game.lenses_time_left--;
12666
12667     if (game.lenses_time_left == 0)
12668       RedrawAllInvisibleElementsForLenses();
12669   }
12670
12671   if (game.magnify_time_left > 0)
12672   {
12673     game.magnify_time_left--;
12674
12675     if (game.magnify_time_left == 0)
12676       RedrawAllInvisibleElementsForMagnifier();
12677   }
12678
12679   for (i = 0; i < MAX_PLAYERS; i++)
12680   {
12681     struct PlayerInfo *player = &stored_player[i];
12682
12683     if (SHIELD_ON(player))
12684     {
12685       if (player->shield_deadly_time_left)
12686         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12687       else if (player->shield_normal_time_left)
12688         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12689     }
12690   }
12691
12692 #if USE_DELAYED_GFX_REDRAW
12693   SCAN_PLAYFIELD(x, y)
12694   {
12695 #if 1
12696     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12697 #else
12698     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12699         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12700 #endif
12701     {
12702       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12703          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12704
12705       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12706         DrawLevelField(x, y);
12707
12708       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12709         DrawLevelFieldCrumbledSand(x, y);
12710
12711       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12712         DrawLevelFieldCrumbledSandNeighbours(x, y);
12713
12714       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12715         DrawTwinkleOnField(x, y);
12716     }
12717
12718     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12719   }
12720 #endif
12721
12722   CheckLevelTime();
12723
12724   DrawAllPlayers();
12725   PlayAllPlayersSound();
12726
12727   if (options.debug)                    /* calculate frames per second */
12728   {
12729     static unsigned long fps_counter = 0;
12730     static int fps_frames = 0;
12731     unsigned long fps_delay_ms = Counter() - fps_counter;
12732
12733     fps_frames++;
12734
12735     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12736     {
12737       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12738
12739       fps_frames = 0;
12740       fps_counter = Counter();
12741     }
12742
12743     redraw_mask |= REDRAW_FPS;
12744   }
12745
12746   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12747
12748   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12749   {
12750     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12751
12752     local_player->show_envelope = 0;
12753   }
12754
12755 #if 0
12756   debug_print_timestamp(0, "stop main loop profiling ");
12757   printf("----------------------------------------------------------\n");
12758 #endif
12759
12760   /* use random number generator in every frame to make it less predictable */
12761   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12762     RND(1);
12763 }
12764
12765 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12766 {
12767   int min_x = x, min_y = y, max_x = x, max_y = y;
12768   int i;
12769
12770   for (i = 0; i < MAX_PLAYERS; i++)
12771   {
12772     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12773
12774     if (!stored_player[i].active || &stored_player[i] == player)
12775       continue;
12776
12777     min_x = MIN(min_x, jx);
12778     min_y = MIN(min_y, jy);
12779     max_x = MAX(max_x, jx);
12780     max_y = MAX(max_y, jy);
12781   }
12782
12783   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12784 }
12785
12786 static boolean AllPlayersInVisibleScreen()
12787 {
12788   int i;
12789
12790   for (i = 0; i < MAX_PLAYERS; i++)
12791   {
12792     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12793
12794     if (!stored_player[i].active)
12795       continue;
12796
12797     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12798       return FALSE;
12799   }
12800
12801   return TRUE;
12802 }
12803
12804 void ScrollLevel(int dx, int dy)
12805 {
12806 #if 0
12807   /* (directly solved in BlitBitmap() now) */
12808   static Bitmap *bitmap_db_field2 = NULL;
12809   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12810   int x, y;
12811 #else
12812   int x, y;
12813 #endif
12814
12815 #if 0
12816   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12817   /* only horizontal XOR vertical scroll direction allowed */
12818   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12819     return;
12820 #endif
12821
12822 #if 0
12823   /* (directly solved in BlitBitmap() now) */
12824   if (bitmap_db_field2 == NULL)
12825     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12826
12827   /* needed when blitting directly to same bitmap -- should not be needed with
12828      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12829   BlitBitmap(drawto_field, bitmap_db_field2,
12830              FX + TILEX * (dx == -1) - softscroll_offset,
12831              FY + TILEY * (dy == -1) - softscroll_offset,
12832              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12833              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12834              FX + TILEX * (dx == 1) - softscroll_offset,
12835              FY + TILEY * (dy == 1) - softscroll_offset);
12836   BlitBitmap(bitmap_db_field2, drawto_field,
12837              FX + TILEX * (dx == 1) - softscroll_offset,
12838              FY + TILEY * (dy == 1) - softscroll_offset,
12839              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12840              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12841              FX + TILEX * (dx == 1) - softscroll_offset,
12842              FY + TILEY * (dy == 1) - softscroll_offset);
12843
12844 #else
12845
12846 #if 0
12847   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12848   int xsize = (BX2 - BX1 + 1);
12849   int ysize = (BY2 - BY1 + 1);
12850   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12851   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12852   int step  = (start < end ? +1 : -1);
12853
12854   for (i = start; i != end; i += step)
12855   {
12856     BlitBitmap(drawto_field, drawto_field,
12857                FX + TILEX * (dx != 0 ? i + step : 0),
12858                FY + TILEY * (dy != 0 ? i + step : 0),
12859                TILEX * (dx != 0 ? 1 : xsize),
12860                TILEY * (dy != 0 ? 1 : ysize),
12861                FX + TILEX * (dx != 0 ? i : 0),
12862                FY + TILEY * (dy != 0 ? i : 0));
12863   }
12864
12865 #else
12866
12867   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12868
12869   BlitBitmap(drawto_field, drawto_field,
12870              FX + TILEX * (dx == -1) - softscroll_offset,
12871              FY + TILEY * (dy == -1) - softscroll_offset,
12872              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12873              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12874              FX + TILEX * (dx == 1) - softscroll_offset,
12875              FY + TILEY * (dy == 1) - softscroll_offset);
12876 #endif
12877 #endif
12878
12879   if (dx != 0)
12880   {
12881     x = (dx == 1 ? BX1 : BX2);
12882     for (y = BY1; y <= BY2; y++)
12883       DrawScreenField(x, y);
12884   }
12885
12886   if (dy != 0)
12887   {
12888     y = (dy == 1 ? BY1 : BY2);
12889     for (x = BX1; x <= BX2; x++)
12890       DrawScreenField(x, y);
12891   }
12892
12893   redraw_mask |= REDRAW_FIELD;
12894 }
12895
12896 static boolean canFallDown(struct PlayerInfo *player)
12897 {
12898   int jx = player->jx, jy = player->jy;
12899
12900   return (IN_LEV_FIELD(jx, jy + 1) &&
12901           (IS_FREE(jx, jy + 1) ||
12902            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12903           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12904           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12905 }
12906
12907 static boolean canPassField(int x, int y, int move_dir)
12908 {
12909   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12910   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12911   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12912   int nextx = x + dx;
12913   int nexty = y + dy;
12914   int element = Feld[x][y];
12915
12916   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12917           !CAN_MOVE(element) &&
12918           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12919           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12920           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12921 }
12922
12923 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12924 {
12925   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12926   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12927   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12928   int newx = x + dx;
12929   int newy = y + dy;
12930
12931   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12932           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12933           (IS_DIGGABLE(Feld[newx][newy]) ||
12934            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12935            canPassField(newx, newy, move_dir)));
12936 }
12937
12938 static void CheckGravityMovement(struct PlayerInfo *player)
12939 {
12940 #if USE_PLAYER_GRAVITY
12941   if (player->gravity && !player->programmed_action)
12942 #else
12943   if (game.gravity && !player->programmed_action)
12944 #endif
12945   {
12946     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12947     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12948     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12949     int jx = player->jx, jy = player->jy;
12950     boolean player_is_moving_to_valid_field =
12951       (!player_is_snapping &&
12952        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12953         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12954     boolean player_can_fall_down = canFallDown(player);
12955
12956     if (player_can_fall_down &&
12957         !player_is_moving_to_valid_field)
12958       player->programmed_action = MV_DOWN;
12959   }
12960 }
12961
12962 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12963 {
12964   return CheckGravityMovement(player);
12965
12966 #if USE_PLAYER_GRAVITY
12967   if (player->gravity && !player->programmed_action)
12968 #else
12969   if (game.gravity && !player->programmed_action)
12970 #endif
12971   {
12972     int jx = player->jx, jy = player->jy;
12973     boolean field_under_player_is_free =
12974       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12975     boolean player_is_standing_on_valid_field =
12976       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12977        (IS_WALKABLE(Feld[jx][jy]) &&
12978         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12979
12980     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12981       player->programmed_action = MV_DOWN;
12982   }
12983 }
12984
12985 /*
12986   MovePlayerOneStep()
12987   -----------------------------------------------------------------------------
12988   dx, dy:               direction (non-diagonal) to try to move the player to
12989   real_dx, real_dy:     direction as read from input device (can be diagonal)
12990 */
12991
12992 boolean MovePlayerOneStep(struct PlayerInfo *player,
12993                           int dx, int dy, int real_dx, int real_dy)
12994 {
12995   int jx = player->jx, jy = player->jy;
12996   int new_jx = jx + dx, new_jy = jy + dy;
12997 #if !USE_FIXED_DONT_RUN_INTO
12998   int element;
12999 #endif
13000   int can_move;
13001   boolean player_can_move = !player->cannot_move;
13002
13003   if (!player->active || (!dx && !dy))
13004     return MP_NO_ACTION;
13005
13006   player->MovDir = (dx < 0 ? MV_LEFT :
13007                     dx > 0 ? MV_RIGHT :
13008                     dy < 0 ? MV_UP :
13009                     dy > 0 ? MV_DOWN :  MV_NONE);
13010
13011   if (!IN_LEV_FIELD(new_jx, new_jy))
13012     return MP_NO_ACTION;
13013
13014   if (!player_can_move)
13015   {
13016     if (player->MovPos == 0)
13017     {
13018       player->is_moving = FALSE;
13019       player->is_digging = FALSE;
13020       player->is_collecting = FALSE;
13021       player->is_snapping = FALSE;
13022       player->is_pushing = FALSE;
13023     }
13024   }
13025
13026 #if 1
13027   if (!options.network && game.centered_player_nr == -1 &&
13028       !AllPlayersInSight(player, new_jx, new_jy))
13029     return MP_NO_ACTION;
13030 #else
13031   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13032     return MP_NO_ACTION;
13033 #endif
13034
13035 #if !USE_FIXED_DONT_RUN_INTO
13036   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13037
13038   /* (moved to DigField()) */
13039   if (player_can_move && DONT_RUN_INTO(element))
13040   {
13041     if (element == EL_ACID && dx == 0 && dy == 1)
13042     {
13043       SplashAcid(new_jx, new_jy);
13044       Feld[jx][jy] = EL_PLAYER_1;
13045       InitMovingField(jx, jy, MV_DOWN);
13046       Store[jx][jy] = EL_ACID;
13047       ContinueMoving(jx, jy);
13048       BuryPlayer(player);
13049     }
13050     else
13051       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13052
13053     return MP_MOVING;
13054   }
13055 #endif
13056
13057   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13058   if (can_move != MP_MOVING)
13059     return can_move;
13060
13061   /* check if DigField() has caused relocation of the player */
13062   if (player->jx != jx || player->jy != jy)
13063     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13064
13065   StorePlayer[jx][jy] = 0;
13066   player->last_jx = jx;
13067   player->last_jy = jy;
13068   player->jx = new_jx;
13069   player->jy = new_jy;
13070   StorePlayer[new_jx][new_jy] = player->element_nr;
13071
13072   if (player->move_delay_value_next != -1)
13073   {
13074     player->move_delay_value = player->move_delay_value_next;
13075     player->move_delay_value_next = -1;
13076   }
13077
13078   player->MovPos =
13079     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13080
13081   player->step_counter++;
13082
13083   PlayerVisit[jx][jy] = FrameCounter;
13084
13085 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13086   player->is_moving = TRUE;
13087 #endif
13088
13089 #if 1
13090   /* should better be called in MovePlayer(), but this breaks some tapes */
13091   ScrollPlayer(player, SCROLL_INIT);
13092 #endif
13093
13094   return MP_MOVING;
13095 }
13096
13097 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13098 {
13099   int jx = player->jx, jy = player->jy;
13100   int old_jx = jx, old_jy = jy;
13101   int moved = MP_NO_ACTION;
13102
13103   if (!player->active)
13104     return FALSE;
13105
13106   if (!dx && !dy)
13107   {
13108     if (player->MovPos == 0)
13109     {
13110       player->is_moving = FALSE;
13111       player->is_digging = FALSE;
13112       player->is_collecting = FALSE;
13113       player->is_snapping = FALSE;
13114       player->is_pushing = FALSE;
13115     }
13116
13117     return FALSE;
13118   }
13119
13120   if (player->move_delay > 0)
13121     return FALSE;
13122
13123   player->move_delay = -1;              /* set to "uninitialized" value */
13124
13125   /* store if player is automatically moved to next field */
13126   player->is_auto_moving = (player->programmed_action != MV_NONE);
13127
13128   /* remove the last programmed player action */
13129   player->programmed_action = 0;
13130
13131   if (player->MovPos)
13132   {
13133     /* should only happen if pre-1.2 tape recordings are played */
13134     /* this is only for backward compatibility */
13135
13136     int original_move_delay_value = player->move_delay_value;
13137
13138 #if DEBUG
13139     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13140            tape.counter);
13141 #endif
13142
13143     /* scroll remaining steps with finest movement resolution */
13144     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13145
13146     while (player->MovPos)
13147     {
13148       ScrollPlayer(player, SCROLL_GO_ON);
13149       ScrollScreen(NULL, SCROLL_GO_ON);
13150
13151       AdvanceFrameAndPlayerCounters(player->index_nr);
13152
13153       DrawAllPlayers();
13154       BackToFront();
13155     }
13156
13157     player->move_delay_value = original_move_delay_value;
13158   }
13159
13160   player->is_active = FALSE;
13161
13162   if (player->last_move_dir & MV_HORIZONTAL)
13163   {
13164     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13165       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13166   }
13167   else
13168   {
13169     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13170       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13171   }
13172
13173 #if USE_FIXED_BORDER_RUNNING_GFX
13174   if (!moved && !player->is_active)
13175   {
13176     player->is_moving = FALSE;
13177     player->is_digging = FALSE;
13178     player->is_collecting = FALSE;
13179     player->is_snapping = FALSE;
13180     player->is_pushing = FALSE;
13181   }
13182 #endif
13183
13184   jx = player->jx;
13185   jy = player->jy;
13186
13187 #if 1
13188   if (moved & MP_MOVING && !ScreenMovPos &&
13189       (player->index_nr == game.centered_player_nr ||
13190        game.centered_player_nr == -1))
13191 #else
13192   if (moved & MP_MOVING && !ScreenMovPos &&
13193       (player == local_player || !options.network))
13194 #endif
13195   {
13196     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13197     int offset = game.scroll_delay_value;
13198
13199     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13200     {
13201       /* actual player has left the screen -- scroll in that direction */
13202       if (jx != old_jx)         /* player has moved horizontally */
13203         scroll_x += (jx - old_jx);
13204       else                      /* player has moved vertically */
13205         scroll_y += (jy - old_jy);
13206     }
13207     else
13208     {
13209       if (jx != old_jx)         /* player has moved horizontally */
13210       {
13211         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13212             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13213           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13214
13215         /* don't scroll over playfield boundaries */
13216         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13217           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13218
13219         /* don't scroll more than one field at a time */
13220         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13221
13222         /* don't scroll against the player's moving direction */
13223         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13224             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13225           scroll_x = old_scroll_x;
13226       }
13227       else                      /* player has moved vertically */
13228       {
13229         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13230             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13231           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13232
13233         /* don't scroll over playfield boundaries */
13234         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13235           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13236
13237         /* don't scroll more than one field at a time */
13238         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13239
13240         /* don't scroll against the player's moving direction */
13241         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13242             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13243           scroll_y = old_scroll_y;
13244       }
13245     }
13246
13247     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13248     {
13249 #if 1
13250       if (!options.network && game.centered_player_nr == -1 &&
13251           !AllPlayersInVisibleScreen())
13252       {
13253         scroll_x = old_scroll_x;
13254         scroll_y = old_scroll_y;
13255       }
13256       else
13257 #else
13258       if (!options.network && !AllPlayersInVisibleScreen())
13259       {
13260         scroll_x = old_scroll_x;
13261         scroll_y = old_scroll_y;
13262       }
13263       else
13264 #endif
13265       {
13266         ScrollScreen(player, SCROLL_INIT);
13267         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13268       }
13269     }
13270   }
13271
13272   player->StepFrame = 0;
13273
13274   if (moved & MP_MOVING)
13275   {
13276     if (old_jx != jx && old_jy == jy)
13277       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13278     else if (old_jx == jx && old_jy != jy)
13279       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13280
13281     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13282
13283     player->last_move_dir = player->MovDir;
13284     player->is_moving = TRUE;
13285     player->is_snapping = FALSE;
13286     player->is_switching = FALSE;
13287     player->is_dropping = FALSE;
13288     player->is_dropping_pressed = FALSE;
13289     player->drop_pressed_delay = 0;
13290
13291 #if 0
13292     /* should better be called here than above, but this breaks some tapes */
13293     ScrollPlayer(player, SCROLL_INIT);
13294 #endif
13295   }
13296   else
13297   {
13298     CheckGravityMovementWhenNotMoving(player);
13299
13300     player->is_moving = FALSE;
13301
13302     /* at this point, the player is allowed to move, but cannot move right now
13303        (e.g. because of something blocking the way) -- ensure that the player
13304        is also allowed to move in the next frame (in old versions before 3.1.1,
13305        the player was forced to wait again for eight frames before next try) */
13306
13307     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13308       player->move_delay = 0;   /* allow direct movement in the next frame */
13309   }
13310
13311   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13312     player->move_delay = player->move_delay_value;
13313
13314   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13315   {
13316     TestIfPlayerTouchesBadThing(jx, jy);
13317     TestIfPlayerTouchesCustomElement(jx, jy);
13318   }
13319
13320   if (!player->active)
13321     RemovePlayer(player);
13322
13323   return moved;
13324 }
13325
13326 void ScrollPlayer(struct PlayerInfo *player, int mode)
13327 {
13328   int jx = player->jx, jy = player->jy;
13329   int last_jx = player->last_jx, last_jy = player->last_jy;
13330   int move_stepsize = TILEX / player->move_delay_value;
13331
13332 #if USE_NEW_PLAYER_SPEED
13333   if (!player->active)
13334     return;
13335
13336   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13337     return;
13338 #else
13339   if (!player->active || player->MovPos == 0)
13340     return;
13341 #endif
13342
13343   if (mode == SCROLL_INIT)
13344   {
13345     player->actual_frame_counter = FrameCounter;
13346     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13347
13348     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13349         Feld[last_jx][last_jy] == EL_EMPTY)
13350     {
13351       int last_field_block_delay = 0;   /* start with no blocking at all */
13352       int block_delay_adjustment = player->block_delay_adjustment;
13353
13354       /* if player blocks last field, add delay for exactly one move */
13355       if (player->block_last_field)
13356       {
13357         last_field_block_delay += player->move_delay_value;
13358
13359         /* when blocking enabled, prevent moving up despite gravity */
13360 #if USE_PLAYER_GRAVITY
13361         if (player->gravity && player->MovDir == MV_UP)
13362           block_delay_adjustment = -1;
13363 #else
13364         if (game.gravity && player->MovDir == MV_UP)
13365           block_delay_adjustment = -1;
13366 #endif
13367       }
13368
13369       /* add block delay adjustment (also possible when not blocking) */
13370       last_field_block_delay += block_delay_adjustment;
13371
13372       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13373       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13374     }
13375
13376 #if USE_NEW_PLAYER_SPEED
13377     if (player->MovPos != 0)    /* player has not yet reached destination */
13378       return;
13379 #else
13380     return;
13381 #endif
13382   }
13383   else if (!FrameReached(&player->actual_frame_counter, 1))
13384     return;
13385
13386 #if USE_NEW_PLAYER_SPEED
13387   if (player->MovPos != 0)
13388   {
13389     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13390     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13391
13392     /* before DrawPlayer() to draw correct player graphic for this case */
13393     if (player->MovPos == 0)
13394       CheckGravityMovement(player);
13395   }
13396 #else
13397   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13398   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13399
13400   /* before DrawPlayer() to draw correct player graphic for this case */
13401   if (player->MovPos == 0)
13402     CheckGravityMovement(player);
13403 #endif
13404
13405   if (player->MovPos == 0)      /* player reached destination field */
13406   {
13407     if (player->move_delay_reset_counter > 0)
13408     {
13409       player->move_delay_reset_counter--;
13410
13411       if (player->move_delay_reset_counter == 0)
13412       {
13413         /* continue with normal speed after quickly moving through gate */
13414         HALVE_PLAYER_SPEED(player);
13415
13416         /* be able to make the next move without delay */
13417         player->move_delay = 0;
13418       }
13419     }
13420
13421     player->last_jx = jx;
13422     player->last_jy = jy;
13423
13424     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13425         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13426         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13427         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13428         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13429         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13430     {
13431       DrawPlayer(player);       /* needed here only to cleanup last field */
13432       RemovePlayer(player);
13433
13434       if (local_player->friends_still_needed == 0 ||
13435           IS_SP_ELEMENT(Feld[jx][jy]))
13436         PlayerWins(player);
13437     }
13438
13439     /* this breaks one level: "machine", level 000 */
13440     {
13441       int move_direction = player->MovDir;
13442       int enter_side = MV_DIR_OPPOSITE(move_direction);
13443       int leave_side = move_direction;
13444       int old_jx = last_jx;
13445       int old_jy = last_jy;
13446       int old_element = Feld[old_jx][old_jy];
13447       int new_element = Feld[jx][jy];
13448
13449       if (IS_CUSTOM_ELEMENT(old_element))
13450         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13451                                    CE_LEFT_BY_PLAYER,
13452                                    player->index_bit, leave_side);
13453
13454       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13455                                           CE_PLAYER_LEAVES_X,
13456                                           player->index_bit, leave_side);
13457
13458       if (IS_CUSTOM_ELEMENT(new_element))
13459         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13460                                    player->index_bit, enter_side);
13461
13462       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13463                                           CE_PLAYER_ENTERS_X,
13464                                           player->index_bit, enter_side);
13465
13466 #if USE_FIX_CE_ACTION_WITH_PLAYER
13467       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13468                                         CE_MOVE_OF_X, move_direction);
13469 #else
13470       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13471                                         CE_MOVE_OF_X, move_direction);
13472 #endif
13473     }
13474
13475     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13476     {
13477       TestIfPlayerTouchesBadThing(jx, jy);
13478       TestIfPlayerTouchesCustomElement(jx, jy);
13479
13480       /* needed because pushed element has not yet reached its destination,
13481          so it would trigger a change event at its previous field location */
13482       if (!player->is_pushing)
13483         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13484
13485       if (!player->active)
13486         RemovePlayer(player);
13487     }
13488
13489     if (!local_player->LevelSolved && level.use_step_counter)
13490     {
13491       int i;
13492
13493       TimePlayed++;
13494
13495       if (TimeLeft > 0)
13496       {
13497         TimeLeft--;
13498
13499         if (TimeLeft <= 10 && setup.time_limit)
13500           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13501
13502 #if 1
13503         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13504
13505         DisplayGameControlValues();
13506 #else
13507         DrawGameValue_Time(TimeLeft);
13508 #endif
13509
13510         if (!TimeLeft && setup.time_limit)
13511           for (i = 0; i < MAX_PLAYERS; i++)
13512             KillPlayer(&stored_player[i]);
13513       }
13514 #if 1
13515       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13516       {
13517         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13518
13519         DisplayGameControlValues();
13520       }
13521 #else
13522       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13523         DrawGameValue_Time(TimePlayed);
13524 #endif
13525     }
13526
13527     if (tape.single_step && tape.recording && !tape.pausing &&
13528         !player->programmed_action)
13529       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13530   }
13531 }
13532
13533 void ScrollScreen(struct PlayerInfo *player, int mode)
13534 {
13535   static unsigned long screen_frame_counter = 0;
13536
13537   if (mode == SCROLL_INIT)
13538   {
13539     /* set scrolling step size according to actual player's moving speed */
13540     ScrollStepSize = TILEX / player->move_delay_value;
13541
13542     screen_frame_counter = FrameCounter;
13543     ScreenMovDir = player->MovDir;
13544     ScreenMovPos = player->MovPos;
13545     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13546     return;
13547   }
13548   else if (!FrameReached(&screen_frame_counter, 1))
13549     return;
13550
13551   if (ScreenMovPos)
13552   {
13553     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13554     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13555     redraw_mask |= REDRAW_FIELD;
13556   }
13557   else
13558     ScreenMovDir = MV_NONE;
13559 }
13560
13561 void TestIfPlayerTouchesCustomElement(int x, int y)
13562 {
13563   static int xy[4][2] =
13564   {
13565     { 0, -1 },
13566     { -1, 0 },
13567     { +1, 0 },
13568     { 0, +1 }
13569   };
13570   static int trigger_sides[4][2] =
13571   {
13572     /* center side       border side */
13573     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13574     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13575     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13576     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13577   };
13578   static int touch_dir[4] =
13579   {
13580     MV_LEFT | MV_RIGHT,
13581     MV_UP   | MV_DOWN,
13582     MV_UP   | MV_DOWN,
13583     MV_LEFT | MV_RIGHT
13584   };
13585   int center_element = Feld[x][y];      /* should always be non-moving! */
13586   int i;
13587
13588   for (i = 0; i < NUM_DIRECTIONS; i++)
13589   {
13590     int xx = x + xy[i][0];
13591     int yy = y + xy[i][1];
13592     int center_side = trigger_sides[i][0];
13593     int border_side = trigger_sides[i][1];
13594     int border_element;
13595
13596     if (!IN_LEV_FIELD(xx, yy))
13597       continue;
13598
13599     if (IS_PLAYER(x, y))                /* player found at center element */
13600     {
13601       struct PlayerInfo *player = PLAYERINFO(x, y);
13602
13603       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13604         border_element = Feld[xx][yy];          /* may be moving! */
13605       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13606         border_element = Feld[xx][yy];
13607       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13608         border_element = MovingOrBlocked2Element(xx, yy);
13609       else
13610         continue;               /* center and border element do not touch */
13611
13612       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13613                                  player->index_bit, border_side);
13614       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13615                                           CE_PLAYER_TOUCHES_X,
13616                                           player->index_bit, border_side);
13617
13618 #if USE_FIX_CE_ACTION_WITH_PLAYER
13619       {
13620         /* use player element that is initially defined in the level playfield,
13621            not the player element that corresponds to the runtime player number
13622            (example: a level that contains EL_PLAYER_3 as the only player would
13623            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13624         int player_element = PLAYERINFO(x, y)->initial_element;
13625
13626         CheckElementChangeBySide(xx, yy, border_element, player_element,
13627                                  CE_TOUCHING_X, border_side);
13628       }
13629 #endif
13630     }
13631     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13632     {
13633       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13634
13635       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13636       {
13637         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13638           continue;             /* center and border element do not touch */
13639       }
13640
13641       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13642                                  player->index_bit, center_side);
13643       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13644                                           CE_PLAYER_TOUCHES_X,
13645                                           player->index_bit, center_side);
13646
13647 #if USE_FIX_CE_ACTION_WITH_PLAYER
13648       {
13649         /* use player element that is initially defined in the level playfield,
13650            not the player element that corresponds to the runtime player number
13651            (example: a level that contains EL_PLAYER_3 as the only player would
13652            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13653         int player_element = PLAYERINFO(xx, yy)->initial_element;
13654
13655         CheckElementChangeBySide(x, y, center_element, player_element,
13656                                  CE_TOUCHING_X, center_side);
13657       }
13658 #endif
13659
13660       break;
13661     }
13662   }
13663 }
13664
13665 #if USE_ELEMENT_TOUCHING_BUGFIX
13666
13667 void TestIfElementTouchesCustomElement(int x, int y)
13668 {
13669   static int xy[4][2] =
13670   {
13671     { 0, -1 },
13672     { -1, 0 },
13673     { +1, 0 },
13674     { 0, +1 }
13675   };
13676   static int trigger_sides[4][2] =
13677   {
13678     /* center side      border side */
13679     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13680     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13681     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13682     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13683   };
13684   static int touch_dir[4] =
13685   {
13686     MV_LEFT | MV_RIGHT,
13687     MV_UP   | MV_DOWN,
13688     MV_UP   | MV_DOWN,
13689     MV_LEFT | MV_RIGHT
13690   };
13691   boolean change_center_element = FALSE;
13692   int center_element = Feld[x][y];      /* should always be non-moving! */
13693   int border_element_old[NUM_DIRECTIONS];
13694   int i;
13695
13696   for (i = 0; i < NUM_DIRECTIONS; i++)
13697   {
13698     int xx = x + xy[i][0];
13699     int yy = y + xy[i][1];
13700     int border_element;
13701
13702     border_element_old[i] = -1;
13703
13704     if (!IN_LEV_FIELD(xx, yy))
13705       continue;
13706
13707     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13708       border_element = Feld[xx][yy];    /* may be moving! */
13709     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13710       border_element = Feld[xx][yy];
13711     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13712       border_element = MovingOrBlocked2Element(xx, yy);
13713     else
13714       continue;                 /* center and border element do not touch */
13715
13716     border_element_old[i] = border_element;
13717   }
13718
13719   for (i = 0; i < NUM_DIRECTIONS; i++)
13720   {
13721     int xx = x + xy[i][0];
13722     int yy = y + xy[i][1];
13723     int center_side = trigger_sides[i][0];
13724     int border_element = border_element_old[i];
13725
13726     if (border_element == -1)
13727       continue;
13728
13729     /* check for change of border element */
13730     CheckElementChangeBySide(xx, yy, border_element, center_element,
13731                              CE_TOUCHING_X, center_side);
13732
13733     /* (center element cannot be player, so we dont have to check this here) */
13734   }
13735
13736   for (i = 0; i < NUM_DIRECTIONS; i++)
13737   {
13738     int xx = x + xy[i][0];
13739     int yy = y + xy[i][1];
13740     int border_side = trigger_sides[i][1];
13741     int border_element = border_element_old[i];
13742
13743     if (border_element == -1)
13744       continue;
13745
13746     /* check for change of center element (but change it only once) */
13747     if (!change_center_element)
13748       change_center_element =
13749         CheckElementChangeBySide(x, y, center_element, border_element,
13750                                  CE_TOUCHING_X, border_side);
13751
13752 #if USE_FIX_CE_ACTION_WITH_PLAYER
13753     if (IS_PLAYER(xx, yy))
13754     {
13755       /* use player element that is initially defined in the level playfield,
13756          not the player element that corresponds to the runtime player number
13757          (example: a level that contains EL_PLAYER_3 as the only player would
13758          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13759       int player_element = PLAYERINFO(xx, yy)->initial_element;
13760
13761       CheckElementChangeBySide(x, y, center_element, player_element,
13762                                CE_TOUCHING_X, border_side);
13763     }
13764 #endif
13765   }
13766 }
13767
13768 #else
13769
13770 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13771 {
13772   static int xy[4][2] =
13773   {
13774     { 0, -1 },
13775     { -1, 0 },
13776     { +1, 0 },
13777     { 0, +1 }
13778   };
13779   static int trigger_sides[4][2] =
13780   {
13781     /* center side      border side */
13782     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13783     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13784     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13785     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13786   };
13787   static int touch_dir[4] =
13788   {
13789     MV_LEFT | MV_RIGHT,
13790     MV_UP   | MV_DOWN,
13791     MV_UP   | MV_DOWN,
13792     MV_LEFT | MV_RIGHT
13793   };
13794   boolean change_center_element = FALSE;
13795   int center_element = Feld[x][y];      /* should always be non-moving! */
13796   int i;
13797
13798   for (i = 0; i < NUM_DIRECTIONS; i++)
13799   {
13800     int xx = x + xy[i][0];
13801     int yy = y + xy[i][1];
13802     int center_side = trigger_sides[i][0];
13803     int border_side = trigger_sides[i][1];
13804     int border_element;
13805
13806     if (!IN_LEV_FIELD(xx, yy))
13807       continue;
13808
13809     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13810       border_element = Feld[xx][yy];    /* may be moving! */
13811     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13812       border_element = Feld[xx][yy];
13813     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13814       border_element = MovingOrBlocked2Element(xx, yy);
13815     else
13816       continue;                 /* center and border element do not touch */
13817
13818     /* check for change of center element (but change it only once) */
13819     if (!change_center_element)
13820       change_center_element =
13821         CheckElementChangeBySide(x, y, center_element, border_element,
13822                                  CE_TOUCHING_X, border_side);
13823
13824     /* check for change of border element */
13825     CheckElementChangeBySide(xx, yy, border_element, center_element,
13826                              CE_TOUCHING_X, center_side);
13827   }
13828 }
13829
13830 #endif
13831
13832 void TestIfElementHitsCustomElement(int x, int y, int direction)
13833 {
13834   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13835   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13836   int hitx = x + dx, hity = y + dy;
13837   int hitting_element = Feld[x][y];
13838   int touched_element;
13839
13840   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13841     return;
13842
13843   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13844                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13845
13846   if (IN_LEV_FIELD(hitx, hity))
13847   {
13848     int opposite_direction = MV_DIR_OPPOSITE(direction);
13849     int hitting_side = direction;
13850     int touched_side = opposite_direction;
13851     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13852                           MovDir[hitx][hity] != direction ||
13853                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13854
13855     object_hit = TRUE;
13856
13857     if (object_hit)
13858     {
13859       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13860                                CE_HITTING_X, touched_side);
13861
13862       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13863                                CE_HIT_BY_X, hitting_side);
13864
13865       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13866                                CE_HIT_BY_SOMETHING, opposite_direction);
13867
13868 #if USE_FIX_CE_ACTION_WITH_PLAYER
13869       if (IS_PLAYER(hitx, hity))
13870       {
13871         /* use player element that is initially defined in the level playfield,
13872            not the player element that corresponds to the runtime player number
13873            (example: a level that contains EL_PLAYER_3 as the only player would
13874            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13875         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13876
13877         CheckElementChangeBySide(x, y, hitting_element, player_element,
13878                                  CE_HITTING_X, touched_side);
13879       }
13880 #endif
13881     }
13882   }
13883
13884   /* "hitting something" is also true when hitting the playfield border */
13885   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13886                            CE_HITTING_SOMETHING, direction);
13887 }
13888
13889 #if 0
13890 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13891 {
13892   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13893   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13894   int hitx = x + dx, hity = y + dy;
13895   int hitting_element = Feld[x][y];
13896   int touched_element;
13897 #if 0
13898   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13899                         !IS_FREE(hitx, hity) &&
13900                         (!IS_MOVING(hitx, hity) ||
13901                          MovDir[hitx][hity] != direction ||
13902                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13903 #endif
13904
13905   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13906     return;
13907
13908 #if 0
13909   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13910     return;
13911 #endif
13912
13913   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13914                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13915
13916   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13917                            EP_CAN_SMASH_EVERYTHING, direction);
13918
13919   if (IN_LEV_FIELD(hitx, hity))
13920   {
13921     int opposite_direction = MV_DIR_OPPOSITE(direction);
13922     int hitting_side = direction;
13923     int touched_side = opposite_direction;
13924 #if 0
13925     int touched_element = MovingOrBlocked2Element(hitx, hity);
13926 #endif
13927 #if 1
13928     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13929                           MovDir[hitx][hity] != direction ||
13930                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13931
13932     object_hit = TRUE;
13933 #endif
13934
13935     if (object_hit)
13936     {
13937       int i;
13938
13939       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13940                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13941
13942       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13943                                CE_OTHER_IS_SMASHING, touched_side);
13944
13945       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13946                                CE_OTHER_GETS_SMASHED, hitting_side);
13947     }
13948   }
13949 }
13950 #endif
13951
13952 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13953 {
13954   int i, kill_x = -1, kill_y = -1;
13955
13956   int bad_element = -1;
13957   static int test_xy[4][2] =
13958   {
13959     { 0, -1 },
13960     { -1, 0 },
13961     { +1, 0 },
13962     { 0, +1 }
13963   };
13964   static int test_dir[4] =
13965   {
13966     MV_UP,
13967     MV_LEFT,
13968     MV_RIGHT,
13969     MV_DOWN
13970   };
13971
13972   for (i = 0; i < NUM_DIRECTIONS; i++)
13973   {
13974     int test_x, test_y, test_move_dir, test_element;
13975
13976     test_x = good_x + test_xy[i][0];
13977     test_y = good_y + test_xy[i][1];
13978
13979     if (!IN_LEV_FIELD(test_x, test_y))
13980       continue;
13981
13982     test_move_dir =
13983       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13984
13985     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13986
13987     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13988        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13989     */
13990     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13991         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13992     {
13993       kill_x = test_x;
13994       kill_y = test_y;
13995       bad_element = test_element;
13996
13997       break;
13998     }
13999   }
14000
14001   if (kill_x != -1 || kill_y != -1)
14002   {
14003     if (IS_PLAYER(good_x, good_y))
14004     {
14005       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14006
14007       if (player->shield_deadly_time_left > 0 &&
14008           !IS_INDESTRUCTIBLE(bad_element))
14009         Bang(kill_x, kill_y);
14010       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14011         KillPlayer(player);
14012     }
14013     else
14014       Bang(good_x, good_y);
14015   }
14016 }
14017
14018 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14019 {
14020   int i, kill_x = -1, kill_y = -1;
14021   int bad_element = Feld[bad_x][bad_y];
14022   static int test_xy[4][2] =
14023   {
14024     { 0, -1 },
14025     { -1, 0 },
14026     { +1, 0 },
14027     { 0, +1 }
14028   };
14029   static int touch_dir[4] =
14030   {
14031     MV_LEFT | MV_RIGHT,
14032     MV_UP   | MV_DOWN,
14033     MV_UP   | MV_DOWN,
14034     MV_LEFT | MV_RIGHT
14035   };
14036   static int test_dir[4] =
14037   {
14038     MV_UP,
14039     MV_LEFT,
14040     MV_RIGHT,
14041     MV_DOWN
14042   };
14043
14044   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14045     return;
14046
14047   for (i = 0; i < NUM_DIRECTIONS; i++)
14048   {
14049     int test_x, test_y, test_move_dir, test_element;
14050
14051     test_x = bad_x + test_xy[i][0];
14052     test_y = bad_y + test_xy[i][1];
14053
14054     if (!IN_LEV_FIELD(test_x, test_y))
14055       continue;
14056
14057     test_move_dir =
14058       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14059
14060     test_element = Feld[test_x][test_y];
14061
14062     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14063        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14064     */
14065     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14066         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14067     {
14068       /* good thing is player or penguin that does not move away */
14069       if (IS_PLAYER(test_x, test_y))
14070       {
14071         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14072
14073         if (bad_element == EL_ROBOT && player->is_moving)
14074           continue;     /* robot does not kill player if he is moving */
14075
14076         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14077         {
14078           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14079             continue;           /* center and border element do not touch */
14080         }
14081
14082         kill_x = test_x;
14083         kill_y = test_y;
14084
14085         break;
14086       }
14087       else if (test_element == EL_PENGUIN)
14088       {
14089         kill_x = test_x;
14090         kill_y = test_y;
14091
14092         break;
14093       }
14094     }
14095   }
14096
14097   if (kill_x != -1 || kill_y != -1)
14098   {
14099     if (IS_PLAYER(kill_x, kill_y))
14100     {
14101       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14102
14103       if (player->shield_deadly_time_left > 0 &&
14104           !IS_INDESTRUCTIBLE(bad_element))
14105         Bang(bad_x, bad_y);
14106       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14107         KillPlayer(player);
14108     }
14109     else
14110       Bang(kill_x, kill_y);
14111   }
14112 }
14113
14114 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14115 {
14116   int bad_element = Feld[bad_x][bad_y];
14117   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14118   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14119   int test_x = bad_x + dx, test_y = bad_y + dy;
14120   int test_move_dir, test_element;
14121   int kill_x = -1, kill_y = -1;
14122
14123   if (!IN_LEV_FIELD(test_x, test_y))
14124     return;
14125
14126   test_move_dir =
14127     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14128
14129   test_element = Feld[test_x][test_y];
14130
14131   if (test_move_dir != bad_move_dir)
14132   {
14133     /* good thing can be player or penguin that does not move away */
14134     if (IS_PLAYER(test_x, test_y))
14135     {
14136       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14137
14138       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14139          player as being hit when he is moving towards the bad thing, because
14140          the "get hit by" condition would be lost after the player stops) */
14141       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14142         return;         /* player moves away from bad thing */
14143
14144       kill_x = test_x;
14145       kill_y = test_y;
14146     }
14147     else if (test_element == EL_PENGUIN)
14148     {
14149       kill_x = test_x;
14150       kill_y = test_y;
14151     }
14152   }
14153
14154   if (kill_x != -1 || kill_y != -1)
14155   {
14156     if (IS_PLAYER(kill_x, kill_y))
14157     {
14158       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14159
14160       if (player->shield_deadly_time_left > 0 &&
14161           !IS_INDESTRUCTIBLE(bad_element))
14162         Bang(bad_x, bad_y);
14163       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14164         KillPlayer(player);
14165     }
14166     else
14167       Bang(kill_x, kill_y);
14168   }
14169 }
14170
14171 void TestIfPlayerTouchesBadThing(int x, int y)
14172 {
14173   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14174 }
14175
14176 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14177 {
14178   TestIfGoodThingHitsBadThing(x, y, move_dir);
14179 }
14180
14181 void TestIfBadThingTouchesPlayer(int x, int y)
14182 {
14183   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14184 }
14185
14186 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14187 {
14188   TestIfBadThingHitsGoodThing(x, y, move_dir);
14189 }
14190
14191 void TestIfFriendTouchesBadThing(int x, int y)
14192 {
14193   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14194 }
14195
14196 void TestIfBadThingTouchesFriend(int x, int y)
14197 {
14198   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14199 }
14200
14201 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14202 {
14203   int i, kill_x = bad_x, kill_y = bad_y;
14204   static int xy[4][2] =
14205   {
14206     { 0, -1 },
14207     { -1, 0 },
14208     { +1, 0 },
14209     { 0, +1 }
14210   };
14211
14212   for (i = 0; i < NUM_DIRECTIONS; i++)
14213   {
14214     int x, y, element;
14215
14216     x = bad_x + xy[i][0];
14217     y = bad_y + xy[i][1];
14218     if (!IN_LEV_FIELD(x, y))
14219       continue;
14220
14221     element = Feld[x][y];
14222     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14223         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14224     {
14225       kill_x = x;
14226       kill_y = y;
14227       break;
14228     }
14229   }
14230
14231   if (kill_x != bad_x || kill_y != bad_y)
14232     Bang(bad_x, bad_y);
14233 }
14234
14235 void KillPlayer(struct PlayerInfo *player)
14236 {
14237   int jx = player->jx, jy = player->jy;
14238
14239   if (!player->active)
14240     return;
14241
14242   /* the following code was introduced to prevent an infinite loop when calling
14243      -> Bang()
14244      -> CheckTriggeredElementChangeExt()
14245      -> ExecuteCustomElementAction()
14246      -> KillPlayer()
14247      -> (infinitely repeating the above sequence of function calls)
14248      which occurs when killing the player while having a CE with the setting
14249      "kill player X when explosion of <player X>"; the solution using a new
14250      field "player->killed" was chosen for backwards compatibility, although
14251      clever use of the fields "player->active" etc. would probably also work */
14252 #if 1
14253   if (player->killed)
14254     return;
14255 #endif
14256
14257   player->killed = TRUE;
14258
14259   /* remove accessible field at the player's position */
14260   Feld[jx][jy] = EL_EMPTY;
14261
14262   /* deactivate shield (else Bang()/Explode() would not work right) */
14263   player->shield_normal_time_left = 0;
14264   player->shield_deadly_time_left = 0;
14265
14266   Bang(jx, jy);
14267
14268 #if USE_PLAYER_REANIMATION
14269   if (player->killed)           /* player may have been reanimated */
14270     BuryPlayer(player);
14271 #else
14272   BuryPlayer(player);
14273 #endif
14274 }
14275
14276 static void KillPlayerUnlessEnemyProtected(int x, int y)
14277 {
14278   if (!PLAYER_ENEMY_PROTECTED(x, y))
14279     KillPlayer(PLAYERINFO(x, y));
14280 }
14281
14282 static void KillPlayerUnlessExplosionProtected(int x, int y)
14283 {
14284   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14285     KillPlayer(PLAYERINFO(x, y));
14286 }
14287
14288 void BuryPlayer(struct PlayerInfo *player)
14289 {
14290   int jx = player->jx, jy = player->jy;
14291
14292   if (!player->active)
14293     return;
14294
14295   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14296   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14297
14298   player->GameOver = TRUE;
14299   RemovePlayer(player);
14300 }
14301
14302 void RemovePlayer(struct PlayerInfo *player)
14303 {
14304   int jx = player->jx, jy = player->jy;
14305   int i, found = FALSE;
14306
14307   player->present = FALSE;
14308   player->active = FALSE;
14309
14310   if (!ExplodeField[jx][jy])
14311     StorePlayer[jx][jy] = 0;
14312
14313   if (player->is_moving)
14314     TEST_DrawLevelField(player->last_jx, player->last_jy);
14315
14316   for (i = 0; i < MAX_PLAYERS; i++)
14317     if (stored_player[i].active)
14318       found = TRUE;
14319
14320   if (!found)
14321     AllPlayersGone = TRUE;
14322
14323   ExitX = ZX = jx;
14324   ExitY = ZY = jy;
14325 }
14326
14327 #if USE_NEW_SNAP_DELAY
14328 static void setFieldForSnapping(int x, int y, int element, int direction)
14329 {
14330   struct ElementInfo *ei = &element_info[element];
14331   int direction_bit = MV_DIR_TO_BIT(direction);
14332   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14333   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14334                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14335
14336   Feld[x][y] = EL_ELEMENT_SNAPPING;
14337   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14338
14339   ResetGfxAnimation(x, y);
14340
14341   GfxElement[x][y] = element;
14342   GfxAction[x][y] = action;
14343   GfxDir[x][y] = direction;
14344   GfxFrame[x][y] = -1;
14345 }
14346 #endif
14347
14348 /*
14349   =============================================================================
14350   checkDiagonalPushing()
14351   -----------------------------------------------------------------------------
14352   check if diagonal input device direction results in pushing of object
14353   (by checking if the alternative direction is walkable, diggable, ...)
14354   =============================================================================
14355 */
14356
14357 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14358                                     int x, int y, int real_dx, int real_dy)
14359 {
14360   int jx, jy, dx, dy, xx, yy;
14361
14362   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14363     return TRUE;
14364
14365   /* diagonal direction: check alternative direction */
14366   jx = player->jx;
14367   jy = player->jy;
14368   dx = x - jx;
14369   dy = y - jy;
14370   xx = jx + (dx == 0 ? real_dx : 0);
14371   yy = jy + (dy == 0 ? real_dy : 0);
14372
14373   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14374 }
14375
14376 /*
14377   =============================================================================
14378   DigField()
14379   -----------------------------------------------------------------------------
14380   x, y:                 field next to player (non-diagonal) to try to dig to
14381   real_dx, real_dy:     direction as read from input device (can be diagonal)
14382   =============================================================================
14383 */
14384
14385 static int DigField(struct PlayerInfo *player,
14386                     int oldx, int oldy, int x, int y,
14387                     int real_dx, int real_dy, int mode)
14388 {
14389   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14390   boolean player_was_pushing = player->is_pushing;
14391   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14392   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14393   int jx = oldx, jy = oldy;
14394   int dx = x - jx, dy = y - jy;
14395   int nextx = x + dx, nexty = y + dy;
14396   int move_direction = (dx == -1 ? MV_LEFT  :
14397                         dx == +1 ? MV_RIGHT :
14398                         dy == -1 ? MV_UP    :
14399                         dy == +1 ? MV_DOWN  : MV_NONE);
14400   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14401   int dig_side = MV_DIR_OPPOSITE(move_direction);
14402   int old_element = Feld[jx][jy];
14403 #if USE_FIXED_DONT_RUN_INTO
14404   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14405 #else
14406   int element;
14407 #endif
14408   int collect_count;
14409
14410   if (is_player)                /* function can also be called by EL_PENGUIN */
14411   {
14412     if (player->MovPos == 0)
14413     {
14414       player->is_digging = FALSE;
14415       player->is_collecting = FALSE;
14416     }
14417
14418     if (player->MovPos == 0)    /* last pushing move finished */
14419       player->is_pushing = FALSE;
14420
14421     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14422     {
14423       player->is_switching = FALSE;
14424       player->push_delay = -1;
14425
14426       return MP_NO_ACTION;
14427     }
14428   }
14429
14430 #if !USE_FIXED_DONT_RUN_INTO
14431   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14432     return MP_NO_ACTION;
14433 #endif
14434
14435   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14436     old_element = Back[jx][jy];
14437
14438   /* in case of element dropped at player position, check background */
14439   else if (Back[jx][jy] != EL_EMPTY &&
14440            game.engine_version >= VERSION_IDENT(2,2,0,0))
14441     old_element = Back[jx][jy];
14442
14443   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14444     return MP_NO_ACTION;        /* field has no opening in this direction */
14445
14446   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14447     return MP_NO_ACTION;        /* field has no opening in this direction */
14448
14449 #if USE_FIXED_DONT_RUN_INTO
14450   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14451   {
14452     SplashAcid(x, y);
14453
14454     Feld[jx][jy] = player->artwork_element;
14455     InitMovingField(jx, jy, MV_DOWN);
14456     Store[jx][jy] = EL_ACID;
14457     ContinueMoving(jx, jy);
14458     BuryPlayer(player);
14459
14460     return MP_DONT_RUN_INTO;
14461   }
14462 #endif
14463
14464 #if USE_FIXED_DONT_RUN_INTO
14465   if (player_can_move && DONT_RUN_INTO(element))
14466   {
14467     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14468
14469     return MP_DONT_RUN_INTO;
14470   }
14471 #endif
14472
14473 #if USE_FIXED_DONT_RUN_INTO
14474   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14475     return MP_NO_ACTION;
14476 #endif
14477
14478 #if !USE_FIXED_DONT_RUN_INTO
14479   element = Feld[x][y];
14480 #endif
14481
14482   collect_count = element_info[element].collect_count_initial;
14483
14484   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14485     return MP_NO_ACTION;
14486
14487   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14488     player_can_move = player_can_move_or_snap;
14489
14490   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14491       game.engine_version >= VERSION_IDENT(2,2,0,0))
14492   {
14493     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14494                                player->index_bit, dig_side);
14495     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14496                                         player->index_bit, dig_side);
14497
14498     if (element == EL_DC_LANDMINE)
14499       Bang(x, y);
14500
14501     if (Feld[x][y] != element)          /* field changed by snapping */
14502       return MP_ACTION;
14503
14504     return MP_NO_ACTION;
14505   }
14506
14507 #if USE_PLAYER_GRAVITY
14508   if (player->gravity && is_player && !player->is_auto_moving &&
14509       canFallDown(player) && move_direction != MV_DOWN &&
14510       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14511     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14512 #else
14513   if (game.gravity && is_player && !player->is_auto_moving &&
14514       canFallDown(player) && move_direction != MV_DOWN &&
14515       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14516     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14517 #endif
14518
14519   if (player_can_move &&
14520       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14521   {
14522     int sound_element = SND_ELEMENT(element);
14523     int sound_action = ACTION_WALKING;
14524
14525     if (IS_RND_GATE(element))
14526     {
14527       if (!player->key[RND_GATE_NR(element)])
14528         return MP_NO_ACTION;
14529     }
14530     else if (IS_RND_GATE_GRAY(element))
14531     {
14532       if (!player->key[RND_GATE_GRAY_NR(element)])
14533         return MP_NO_ACTION;
14534     }
14535     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14536     {
14537       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14538         return MP_NO_ACTION;
14539     }
14540     else if (element == EL_EXIT_OPEN ||
14541              element == EL_EM_EXIT_OPEN ||
14542              element == EL_STEEL_EXIT_OPEN ||
14543              element == EL_EM_STEEL_EXIT_OPEN ||
14544              element == EL_SP_EXIT_OPEN ||
14545              element == EL_SP_EXIT_OPENING)
14546     {
14547       sound_action = ACTION_PASSING;    /* player is passing exit */
14548     }
14549     else if (element == EL_EMPTY)
14550     {
14551       sound_action = ACTION_MOVING;             /* nothing to walk on */
14552     }
14553
14554     /* play sound from background or player, whatever is available */
14555     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14556       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14557     else
14558       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14559   }
14560   else if (player_can_move &&
14561            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14562   {
14563     if (!ACCESS_FROM(element, opposite_direction))
14564       return MP_NO_ACTION;      /* field not accessible from this direction */
14565
14566     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14567       return MP_NO_ACTION;
14568
14569     if (IS_EM_GATE(element))
14570     {
14571       if (!player->key[EM_GATE_NR(element)])
14572         return MP_NO_ACTION;
14573     }
14574     else if (IS_EM_GATE_GRAY(element))
14575     {
14576       if (!player->key[EM_GATE_GRAY_NR(element)])
14577         return MP_NO_ACTION;
14578     }
14579     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14580     {
14581       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14582         return MP_NO_ACTION;
14583     }
14584     else if (IS_EMC_GATE(element))
14585     {
14586       if (!player->key[EMC_GATE_NR(element)])
14587         return MP_NO_ACTION;
14588     }
14589     else if (IS_EMC_GATE_GRAY(element))
14590     {
14591       if (!player->key[EMC_GATE_GRAY_NR(element)])
14592         return MP_NO_ACTION;
14593     }
14594     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14595     {
14596       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14597         return MP_NO_ACTION;
14598     }
14599     else if (element == EL_DC_GATE_WHITE ||
14600              element == EL_DC_GATE_WHITE_GRAY ||
14601              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14602     {
14603       if (player->num_white_keys == 0)
14604         return MP_NO_ACTION;
14605
14606       player->num_white_keys--;
14607     }
14608     else if (IS_SP_PORT(element))
14609     {
14610       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14611           element == EL_SP_GRAVITY_PORT_RIGHT ||
14612           element == EL_SP_GRAVITY_PORT_UP ||
14613           element == EL_SP_GRAVITY_PORT_DOWN)
14614 #if USE_PLAYER_GRAVITY
14615         player->gravity = !player->gravity;
14616 #else
14617         game.gravity = !game.gravity;
14618 #endif
14619       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14620                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14621                element == EL_SP_GRAVITY_ON_PORT_UP ||
14622                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14623 #if USE_PLAYER_GRAVITY
14624         player->gravity = TRUE;
14625 #else
14626         game.gravity = TRUE;
14627 #endif
14628       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14629                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14630                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14631                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14632 #if USE_PLAYER_GRAVITY
14633         player->gravity = FALSE;
14634 #else
14635         game.gravity = FALSE;
14636 #endif
14637     }
14638
14639     /* automatically move to the next field with double speed */
14640     player->programmed_action = move_direction;
14641
14642     if (player->move_delay_reset_counter == 0)
14643     {
14644       player->move_delay_reset_counter = 2;     /* two double speed steps */
14645
14646       DOUBLE_PLAYER_SPEED(player);
14647     }
14648
14649     PlayLevelSoundAction(x, y, ACTION_PASSING);
14650   }
14651   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14652   {
14653     RemoveField(x, y);
14654
14655     if (mode != DF_SNAP)
14656     {
14657       GfxElement[x][y] = GFX_ELEMENT(element);
14658       player->is_digging = TRUE;
14659     }
14660
14661     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14662
14663     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14664                                         player->index_bit, dig_side);
14665
14666     if (mode == DF_SNAP)
14667     {
14668 #if USE_NEW_SNAP_DELAY
14669       if (level.block_snap_field)
14670         setFieldForSnapping(x, y, element, move_direction);
14671       else
14672         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14673 #else
14674       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14675 #endif
14676
14677       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14678                                           player->index_bit, dig_side);
14679     }
14680   }
14681   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14682   {
14683     RemoveField(x, y);
14684
14685     if (is_player && mode != DF_SNAP)
14686     {
14687       GfxElement[x][y] = element;
14688       player->is_collecting = TRUE;
14689     }
14690
14691     if (element == EL_SPEED_PILL)
14692     {
14693       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14694     }
14695     else if (element == EL_EXTRA_TIME && level.time > 0)
14696     {
14697       TimeLeft += level.extra_time;
14698
14699 #if 1
14700       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14701
14702       DisplayGameControlValues();
14703 #else
14704       DrawGameValue_Time(TimeLeft);
14705 #endif
14706     }
14707     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14708     {
14709       player->shield_normal_time_left += level.shield_normal_time;
14710       if (element == EL_SHIELD_DEADLY)
14711         player->shield_deadly_time_left += level.shield_deadly_time;
14712     }
14713     else if (element == EL_DYNAMITE ||
14714              element == EL_EM_DYNAMITE ||
14715              element == EL_SP_DISK_RED)
14716     {
14717       if (player->inventory_size < MAX_INVENTORY_SIZE)
14718         player->inventory_element[player->inventory_size++] = element;
14719
14720       DrawGameDoorValues();
14721     }
14722     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14723     {
14724       player->dynabomb_count++;
14725       player->dynabombs_left++;
14726     }
14727     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14728     {
14729       player->dynabomb_size++;
14730     }
14731     else if (element == EL_DYNABOMB_INCREASE_POWER)
14732     {
14733       player->dynabomb_xl = TRUE;
14734     }
14735     else if (IS_KEY(element))
14736     {
14737       player->key[KEY_NR(element)] = TRUE;
14738
14739       DrawGameDoorValues();
14740     }
14741     else if (element == EL_DC_KEY_WHITE)
14742     {
14743       player->num_white_keys++;
14744
14745       /* display white keys? */
14746       /* DrawGameDoorValues(); */
14747     }
14748     else if (IS_ENVELOPE(element))
14749     {
14750       player->show_envelope = element;
14751     }
14752     else if (element == EL_EMC_LENSES)
14753     {
14754       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14755
14756       RedrawAllInvisibleElementsForLenses();
14757     }
14758     else if (element == EL_EMC_MAGNIFIER)
14759     {
14760       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14761
14762       RedrawAllInvisibleElementsForMagnifier();
14763     }
14764     else if (IS_DROPPABLE(element) ||
14765              IS_THROWABLE(element))     /* can be collected and dropped */
14766     {
14767       int i;
14768
14769       if (collect_count == 0)
14770         player->inventory_infinite_element = element;
14771       else
14772         for (i = 0; i < collect_count; i++)
14773           if (player->inventory_size < MAX_INVENTORY_SIZE)
14774             player->inventory_element[player->inventory_size++] = element;
14775
14776       DrawGameDoorValues();
14777     }
14778     else if (collect_count > 0)
14779     {
14780       local_player->gems_still_needed -= collect_count;
14781       if (local_player->gems_still_needed < 0)
14782         local_player->gems_still_needed = 0;
14783
14784 #if 1
14785       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14786
14787       DisplayGameControlValues();
14788 #else
14789       DrawGameValue_Emeralds(local_player->gems_still_needed);
14790 #endif
14791     }
14792
14793     RaiseScoreElement(element);
14794     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14795
14796     if (is_player)
14797       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14798                                           player->index_bit, dig_side);
14799
14800     if (mode == DF_SNAP)
14801     {
14802 #if USE_NEW_SNAP_DELAY
14803       if (level.block_snap_field)
14804         setFieldForSnapping(x, y, element, move_direction);
14805       else
14806         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14807 #else
14808       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14809 #endif
14810
14811       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14812                                           player->index_bit, dig_side);
14813     }
14814   }
14815   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14816   {
14817     if (mode == DF_SNAP && element != EL_BD_ROCK)
14818       return MP_NO_ACTION;
14819
14820     if (CAN_FALL(element) && dy)
14821       return MP_NO_ACTION;
14822
14823     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14824         !(element == EL_SPRING && level.use_spring_bug))
14825       return MP_NO_ACTION;
14826
14827     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14828         ((move_direction & MV_VERTICAL &&
14829           ((element_info[element].move_pattern & MV_LEFT &&
14830             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14831            (element_info[element].move_pattern & MV_RIGHT &&
14832             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14833          (move_direction & MV_HORIZONTAL &&
14834           ((element_info[element].move_pattern & MV_UP &&
14835             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14836            (element_info[element].move_pattern & MV_DOWN &&
14837             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14838       return MP_NO_ACTION;
14839
14840     /* do not push elements already moving away faster than player */
14841     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14842         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14843       return MP_NO_ACTION;
14844
14845     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14846     {
14847       if (player->push_delay_value == -1 || !player_was_pushing)
14848         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14849     }
14850     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14851     {
14852       if (player->push_delay_value == -1)
14853         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14854     }
14855     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14856     {
14857       if (!player->is_pushing)
14858         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14859     }
14860
14861     player->is_pushing = TRUE;
14862     player->is_active = TRUE;
14863
14864     if (!(IN_LEV_FIELD(nextx, nexty) &&
14865           (IS_FREE(nextx, nexty) ||
14866            (IS_SB_ELEMENT(element) &&
14867             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14868            (IS_CUSTOM_ELEMENT(element) &&
14869             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14870       return MP_NO_ACTION;
14871
14872     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14873       return MP_NO_ACTION;
14874
14875     if (player->push_delay == -1)       /* new pushing; restart delay */
14876       player->push_delay = 0;
14877
14878     if (player->push_delay < player->push_delay_value &&
14879         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14880         element != EL_SPRING && element != EL_BALLOON)
14881     {
14882       /* make sure that there is no move delay before next try to push */
14883       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14884         player->move_delay = 0;
14885
14886       return MP_NO_ACTION;
14887     }
14888
14889     if (IS_CUSTOM_ELEMENT(element) &&
14890         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14891     {
14892       if (!DigFieldByCE(nextx, nexty, element))
14893         return MP_NO_ACTION;
14894     }
14895
14896     if (IS_SB_ELEMENT(element))
14897     {
14898       if (element == EL_SOKOBAN_FIELD_FULL)
14899       {
14900         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14901         local_player->sokobanfields_still_needed++;
14902       }
14903
14904       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14905       {
14906         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14907         local_player->sokobanfields_still_needed--;
14908       }
14909
14910       Feld[x][y] = EL_SOKOBAN_OBJECT;
14911
14912       if (Back[x][y] == Back[nextx][nexty])
14913         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14914       else if (Back[x][y] != 0)
14915         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14916                                     ACTION_EMPTYING);
14917       else
14918         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14919                                     ACTION_FILLING);
14920
14921       if (local_player->sokobanfields_still_needed == 0 &&
14922           game.emulation == EMU_SOKOBAN)
14923       {
14924         PlayerWins(player);
14925
14926         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14927       }
14928     }
14929     else
14930       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14931
14932     InitMovingField(x, y, move_direction);
14933     GfxAction[x][y] = ACTION_PUSHING;
14934
14935     if (mode == DF_SNAP)
14936       ContinueMoving(x, y);
14937     else
14938       MovPos[x][y] = (dx != 0 ? dx : dy);
14939
14940     Pushed[x][y] = TRUE;
14941     Pushed[nextx][nexty] = TRUE;
14942
14943     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14944       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14945     else
14946       player->push_delay_value = -1;    /* get new value later */
14947
14948     /* check for element change _after_ element has been pushed */
14949     if (game.use_change_when_pushing_bug)
14950     {
14951       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14952                                  player->index_bit, dig_side);
14953       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14954                                           player->index_bit, dig_side);
14955     }
14956   }
14957   else if (IS_SWITCHABLE(element))
14958   {
14959     if (PLAYER_SWITCHING(player, x, y))
14960     {
14961       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14962                                           player->index_bit, dig_side);
14963
14964       return MP_ACTION;
14965     }
14966
14967     player->is_switching = TRUE;
14968     player->switch_x = x;
14969     player->switch_y = y;
14970
14971     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14972
14973     if (element == EL_ROBOT_WHEEL)
14974     {
14975       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14976       ZX = x;
14977       ZY = y;
14978
14979       game.robot_wheel_active = TRUE;
14980
14981       TEST_DrawLevelField(x, y);
14982     }
14983     else if (element == EL_SP_TERMINAL)
14984     {
14985       int xx, yy;
14986
14987       SCAN_PLAYFIELD(xx, yy)
14988       {
14989         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14990           Bang(xx, yy);
14991         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14992           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14993       }
14994     }
14995     else if (IS_BELT_SWITCH(element))
14996     {
14997       ToggleBeltSwitch(x, y);
14998     }
14999     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15000              element == EL_SWITCHGATE_SWITCH_DOWN ||
15001              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15002              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15003     {
15004       ToggleSwitchgateSwitch(x, y);
15005     }
15006     else if (element == EL_LIGHT_SWITCH ||
15007              element == EL_LIGHT_SWITCH_ACTIVE)
15008     {
15009       ToggleLightSwitch(x, y);
15010     }
15011     else if (element == EL_TIMEGATE_SWITCH ||
15012              element == EL_DC_TIMEGATE_SWITCH)
15013     {
15014       ActivateTimegateSwitch(x, y);
15015     }
15016     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15017              element == EL_BALLOON_SWITCH_RIGHT ||
15018              element == EL_BALLOON_SWITCH_UP    ||
15019              element == EL_BALLOON_SWITCH_DOWN  ||
15020              element == EL_BALLOON_SWITCH_NONE  ||
15021              element == EL_BALLOON_SWITCH_ANY)
15022     {
15023       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15024                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15025                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15026                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15027                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15028                              move_direction);
15029     }
15030     else if (element == EL_LAMP)
15031     {
15032       Feld[x][y] = EL_LAMP_ACTIVE;
15033       local_player->lights_still_needed--;
15034
15035       ResetGfxAnimation(x, y);
15036       TEST_DrawLevelField(x, y);
15037     }
15038     else if (element == EL_TIME_ORB_FULL)
15039     {
15040       Feld[x][y] = EL_TIME_ORB_EMPTY;
15041
15042       if (level.time > 0 || level.use_time_orb_bug)
15043       {
15044         TimeLeft += level.time_orb_time;
15045
15046 #if 1
15047         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15048
15049         DisplayGameControlValues();
15050 #else
15051         DrawGameValue_Time(TimeLeft);
15052 #endif
15053       }
15054
15055       ResetGfxAnimation(x, y);
15056       TEST_DrawLevelField(x, y);
15057     }
15058     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15059              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15060     {
15061       int xx, yy;
15062
15063       game.ball_state = !game.ball_state;
15064
15065       SCAN_PLAYFIELD(xx, yy)
15066       {
15067         int e = Feld[xx][yy];
15068
15069         if (game.ball_state)
15070         {
15071           if (e == EL_EMC_MAGIC_BALL)
15072             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15073           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15074             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15075         }
15076         else
15077         {
15078           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15079             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15080           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15081             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15082         }
15083       }
15084     }
15085
15086     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15087                                         player->index_bit, dig_side);
15088
15089     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15090                                         player->index_bit, dig_side);
15091
15092     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15093                                         player->index_bit, dig_side);
15094
15095     return MP_ACTION;
15096   }
15097   else
15098   {
15099     if (!PLAYER_SWITCHING(player, x, y))
15100     {
15101       player->is_switching = TRUE;
15102       player->switch_x = x;
15103       player->switch_y = y;
15104
15105       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15106                                  player->index_bit, dig_side);
15107       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15108                                           player->index_bit, dig_side);
15109
15110       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15111                                  player->index_bit, dig_side);
15112       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15113                                           player->index_bit, dig_side);
15114     }
15115
15116     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15117                                player->index_bit, dig_side);
15118     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15119                                         player->index_bit, dig_side);
15120
15121     return MP_NO_ACTION;
15122   }
15123
15124   player->push_delay = -1;
15125
15126   if (is_player)                /* function can also be called by EL_PENGUIN */
15127   {
15128     if (Feld[x][y] != element)          /* really digged/collected something */
15129     {
15130       player->is_collecting = !player->is_digging;
15131       player->is_active = TRUE;
15132     }
15133   }
15134
15135   return MP_MOVING;
15136 }
15137
15138 static boolean DigFieldByCE(int x, int y, int digging_element)
15139 {
15140   int element = Feld[x][y];
15141
15142   if (!IS_FREE(x, y))
15143   {
15144     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15145                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15146                   ACTION_BREAKING);
15147
15148     /* no element can dig solid indestructible elements */
15149     if (IS_INDESTRUCTIBLE(element) &&
15150         !IS_DIGGABLE(element) &&
15151         !IS_COLLECTIBLE(element))
15152       return FALSE;
15153
15154     if (AmoebaNr[x][y] &&
15155         (element == EL_AMOEBA_FULL ||
15156          element == EL_BD_AMOEBA ||
15157          element == EL_AMOEBA_GROWING))
15158     {
15159       AmoebaCnt[AmoebaNr[x][y]]--;
15160       AmoebaCnt2[AmoebaNr[x][y]]--;
15161     }
15162
15163     if (IS_MOVING(x, y))
15164       RemoveMovingField(x, y);
15165     else
15166     {
15167       RemoveField(x, y);
15168       TEST_DrawLevelField(x, y);
15169     }
15170
15171     /* if digged element was about to explode, prevent the explosion */
15172     ExplodeField[x][y] = EX_TYPE_NONE;
15173
15174     PlayLevelSoundAction(x, y, action);
15175   }
15176
15177   Store[x][y] = EL_EMPTY;
15178
15179 #if 1
15180   /* this makes it possible to leave the removed element again */
15181   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15182     Store[x][y] = element;
15183 #else
15184   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15185   {
15186     int move_leave_element = element_info[digging_element].move_leave_element;
15187
15188     /* this makes it possible to leave the removed element again */
15189     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15190                    element : move_leave_element);
15191   }
15192 #endif
15193
15194   return TRUE;
15195 }
15196
15197 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15198 {
15199   int jx = player->jx, jy = player->jy;
15200   int x = jx + dx, y = jy + dy;
15201   int snap_direction = (dx == -1 ? MV_LEFT  :
15202                         dx == +1 ? MV_RIGHT :
15203                         dy == -1 ? MV_UP    :
15204                         dy == +1 ? MV_DOWN  : MV_NONE);
15205   boolean can_continue_snapping = (level.continuous_snapping &&
15206                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15207
15208   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15209     return FALSE;
15210
15211   if (!player->active || !IN_LEV_FIELD(x, y))
15212     return FALSE;
15213
15214   if (dx && dy)
15215     return FALSE;
15216
15217   if (!dx && !dy)
15218   {
15219     if (player->MovPos == 0)
15220       player->is_pushing = FALSE;
15221
15222     player->is_snapping = FALSE;
15223
15224     if (player->MovPos == 0)
15225     {
15226       player->is_moving = FALSE;
15227       player->is_digging = FALSE;
15228       player->is_collecting = FALSE;
15229     }
15230
15231     return FALSE;
15232   }
15233
15234 #if USE_NEW_CONTINUOUS_SNAPPING
15235   /* prevent snapping with already pressed snap key when not allowed */
15236   if (player->is_snapping && !can_continue_snapping)
15237     return FALSE;
15238 #else
15239   if (player->is_snapping)
15240     return FALSE;
15241 #endif
15242
15243   player->MovDir = snap_direction;
15244
15245   if (player->MovPos == 0)
15246   {
15247     player->is_moving = FALSE;
15248     player->is_digging = FALSE;
15249     player->is_collecting = FALSE;
15250   }
15251
15252   player->is_dropping = FALSE;
15253   player->is_dropping_pressed = FALSE;
15254   player->drop_pressed_delay = 0;
15255
15256   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15257     return FALSE;
15258
15259   player->is_snapping = TRUE;
15260   player->is_active = TRUE;
15261
15262   if (player->MovPos == 0)
15263   {
15264     player->is_moving = FALSE;
15265     player->is_digging = FALSE;
15266     player->is_collecting = FALSE;
15267   }
15268
15269   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15270     TEST_DrawLevelField(player->last_jx, player->last_jy);
15271
15272   TEST_DrawLevelField(x, y);
15273
15274   return TRUE;
15275 }
15276
15277 static boolean DropElement(struct PlayerInfo *player)
15278 {
15279   int old_element, new_element;
15280   int dropx = player->jx, dropy = player->jy;
15281   int drop_direction = player->MovDir;
15282   int drop_side = drop_direction;
15283 #if 1
15284   int drop_element = get_next_dropped_element(player);
15285 #else
15286   int drop_element = (player->inventory_size > 0 ?
15287                       player->inventory_element[player->inventory_size - 1] :
15288                       player->inventory_infinite_element != EL_UNDEFINED ?
15289                       player->inventory_infinite_element :
15290                       player->dynabombs_left > 0 ?
15291                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15292                       EL_UNDEFINED);
15293 #endif
15294
15295   player->is_dropping_pressed = TRUE;
15296
15297   /* do not drop an element on top of another element; when holding drop key
15298      pressed without moving, dropped element must move away before the next
15299      element can be dropped (this is especially important if the next element
15300      is dynamite, which can be placed on background for historical reasons) */
15301   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15302     return MP_ACTION;
15303
15304   if (IS_THROWABLE(drop_element))
15305   {
15306     dropx += GET_DX_FROM_DIR(drop_direction);
15307     dropy += GET_DY_FROM_DIR(drop_direction);
15308
15309     if (!IN_LEV_FIELD(dropx, dropy))
15310       return FALSE;
15311   }
15312
15313   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15314   new_element = drop_element;           /* default: no change when dropping */
15315
15316   /* check if player is active, not moving and ready to drop */
15317   if (!player->active || player->MovPos || player->drop_delay > 0)
15318     return FALSE;
15319
15320   /* check if player has anything that can be dropped */
15321   if (new_element == EL_UNDEFINED)
15322     return FALSE;
15323
15324   /* check if drop key was pressed long enough for EM style dynamite */
15325   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15326     return FALSE;
15327
15328   /* check if anything can be dropped at the current position */
15329   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15330     return FALSE;
15331
15332   /* collected custom elements can only be dropped on empty fields */
15333   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15334     return FALSE;
15335
15336   if (old_element != EL_EMPTY)
15337     Back[dropx][dropy] = old_element;   /* store old element on this field */
15338
15339   ResetGfxAnimation(dropx, dropy);
15340   ResetRandomAnimationValue(dropx, dropy);
15341
15342   if (player->inventory_size > 0 ||
15343       player->inventory_infinite_element != EL_UNDEFINED)
15344   {
15345     if (player->inventory_size > 0)
15346     {
15347       player->inventory_size--;
15348
15349       DrawGameDoorValues();
15350
15351       if (new_element == EL_DYNAMITE)
15352         new_element = EL_DYNAMITE_ACTIVE;
15353       else if (new_element == EL_EM_DYNAMITE)
15354         new_element = EL_EM_DYNAMITE_ACTIVE;
15355       else if (new_element == EL_SP_DISK_RED)
15356         new_element = EL_SP_DISK_RED_ACTIVE;
15357     }
15358
15359     Feld[dropx][dropy] = new_element;
15360
15361     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15362       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15363                           el2img(Feld[dropx][dropy]), 0);
15364
15365     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15366
15367     /* needed if previous element just changed to "empty" in the last frame */
15368     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15369
15370     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15371                                player->index_bit, drop_side);
15372     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15373                                         CE_PLAYER_DROPS_X,
15374                                         player->index_bit, drop_side);
15375
15376     TestIfElementTouchesCustomElement(dropx, dropy);
15377   }
15378   else          /* player is dropping a dyna bomb */
15379   {
15380     player->dynabombs_left--;
15381
15382     Feld[dropx][dropy] = new_element;
15383
15384     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15385       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15386                           el2img(Feld[dropx][dropy]), 0);
15387
15388     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15389   }
15390
15391   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15392     InitField_WithBug1(dropx, dropy, FALSE);
15393
15394   new_element = Feld[dropx][dropy];     /* element might have changed */
15395
15396   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15397       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15398   {
15399     int move_direction, nextx, nexty;
15400
15401     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15402       MovDir[dropx][dropy] = drop_direction;
15403
15404     move_direction = MovDir[dropx][dropy];
15405     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15406     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15407
15408     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15409
15410 #if USE_FIX_IMPACT_COLLISION
15411     /* do not cause impact style collision by dropping elements that can fall */
15412     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15413 #else
15414     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15415 #endif
15416   }
15417
15418   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15419   player->is_dropping = TRUE;
15420
15421   player->drop_pressed_delay = 0;
15422   player->is_dropping_pressed = FALSE;
15423
15424   player->drop_x = dropx;
15425   player->drop_y = dropy;
15426
15427   return TRUE;
15428 }
15429
15430 /* ------------------------------------------------------------------------- */
15431 /* game sound playing functions                                              */
15432 /* ------------------------------------------------------------------------- */
15433
15434 static int *loop_sound_frame = NULL;
15435 static int *loop_sound_volume = NULL;
15436
15437 void InitPlayLevelSound()
15438 {
15439   int num_sounds = getSoundListSize();
15440
15441   checked_free(loop_sound_frame);
15442   checked_free(loop_sound_volume);
15443
15444   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15445   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15446 }
15447
15448 static void PlayLevelSound(int x, int y, int nr)
15449 {
15450   int sx = SCREENX(x), sy = SCREENY(y);
15451   int volume, stereo_position;
15452   int max_distance = 8;
15453   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15454
15455   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15456       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15457     return;
15458
15459   if (!IN_LEV_FIELD(x, y) ||
15460       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15461       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15462     return;
15463
15464   volume = SOUND_MAX_VOLUME;
15465
15466   if (!IN_SCR_FIELD(sx, sy))
15467   {
15468     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15469     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15470
15471     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15472   }
15473
15474   stereo_position = (SOUND_MAX_LEFT +
15475                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15476                      (SCR_FIELDX + 2 * max_distance));
15477
15478   if (IS_LOOP_SOUND(nr))
15479   {
15480     /* This assures that quieter loop sounds do not overwrite louder ones,
15481        while restarting sound volume comparison with each new game frame. */
15482
15483     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15484       return;
15485
15486     loop_sound_volume[nr] = volume;
15487     loop_sound_frame[nr] = FrameCounter;
15488   }
15489
15490   PlaySoundExt(nr, volume, stereo_position, type);
15491 }
15492
15493 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15494 {
15495   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15496                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15497                  y < LEVELY(BY1) ? LEVELY(BY1) :
15498                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15499                  sound_action);
15500 }
15501
15502 static void PlayLevelSoundAction(int x, int y, int action)
15503 {
15504   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15505 }
15506
15507 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15508 {
15509   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15510
15511   if (sound_effect != SND_UNDEFINED)
15512     PlayLevelSound(x, y, sound_effect);
15513 }
15514
15515 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15516                                               int action)
15517 {
15518   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15519
15520   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15521     PlayLevelSound(x, y, sound_effect);
15522 }
15523
15524 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15525 {
15526   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15527
15528   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15529     PlayLevelSound(x, y, sound_effect);
15530 }
15531
15532 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15533 {
15534   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15535
15536   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15537     StopSound(sound_effect);
15538 }
15539
15540 static void PlayLevelMusic()
15541 {
15542   if (levelset.music[level_nr] != MUS_UNDEFINED)
15543     PlayMusic(levelset.music[level_nr]);        /* from config file */
15544   else
15545     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15546 }
15547
15548 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15549 {
15550   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15551   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15552   int x = xx - 1 - offset;
15553   int y = yy - 1 - offset;
15554
15555   switch (sample)
15556   {
15557     case SAMPLE_blank:
15558       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15559       break;
15560
15561     case SAMPLE_roll:
15562       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15563       break;
15564
15565     case SAMPLE_stone:
15566       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15567       break;
15568
15569     case SAMPLE_nut:
15570       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15571       break;
15572
15573     case SAMPLE_crack:
15574       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15575       break;
15576
15577     case SAMPLE_bug:
15578       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15579       break;
15580
15581     case SAMPLE_tank:
15582       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15583       break;
15584
15585     case SAMPLE_android_clone:
15586       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15587       break;
15588
15589     case SAMPLE_android_move:
15590       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15591       break;
15592
15593     case SAMPLE_spring:
15594       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15595       break;
15596
15597     case SAMPLE_slurp:
15598       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15599       break;
15600
15601     case SAMPLE_eater:
15602       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15603       break;
15604
15605     case SAMPLE_eater_eat:
15606       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15607       break;
15608
15609     case SAMPLE_alien:
15610       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15611       break;
15612
15613     case SAMPLE_collect:
15614       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15615       break;
15616
15617     case SAMPLE_diamond:
15618       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15619       break;
15620
15621     case SAMPLE_squash:
15622       /* !!! CHECK THIS !!! */
15623 #if 1
15624       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15625 #else
15626       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15627 #endif
15628       break;
15629
15630     case SAMPLE_wonderfall:
15631       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15632       break;
15633
15634     case SAMPLE_drip:
15635       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15636       break;
15637
15638     case SAMPLE_push:
15639       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15640       break;
15641
15642     case SAMPLE_dirt:
15643       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15644       break;
15645
15646     case SAMPLE_acid:
15647       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15648       break;
15649
15650     case SAMPLE_ball:
15651       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15652       break;
15653
15654     case SAMPLE_grow:
15655       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15656       break;
15657
15658     case SAMPLE_wonder:
15659       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15660       break;
15661
15662     case SAMPLE_door:
15663       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15664       break;
15665
15666     case SAMPLE_exit_open:
15667       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15668       break;
15669
15670     case SAMPLE_exit_leave:
15671       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15672       break;
15673
15674     case SAMPLE_dynamite:
15675       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15676       break;
15677
15678     case SAMPLE_tick:
15679       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15680       break;
15681
15682     case SAMPLE_press:
15683       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15684       break;
15685
15686     case SAMPLE_wheel:
15687       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15688       break;
15689
15690     case SAMPLE_boom:
15691       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15692       break;
15693
15694     case SAMPLE_die:
15695       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15696       break;
15697
15698     case SAMPLE_time:
15699       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15700       break;
15701
15702     default:
15703       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15704       break;
15705   }
15706 }
15707
15708 #if 0
15709 void ChangeTime(int value)
15710 {
15711   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15712
15713   *time += value;
15714
15715   /* EMC game engine uses value from time counter of RND game engine */
15716   level.native_em_level->lev->time = *time;
15717
15718   DrawGameValue_Time(*time);
15719 }
15720
15721 void RaiseScore(int value)
15722 {
15723   /* EMC game engine and RND game engine have separate score counters */
15724   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15725                 &level.native_em_level->lev->score : &local_player->score);
15726
15727   *score += value;
15728
15729   DrawGameValue_Score(*score);
15730 }
15731 #endif
15732
15733 void RaiseScore(int value)
15734 {
15735   local_player->score += value;
15736
15737 #if 1
15738   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15739
15740   DisplayGameControlValues();
15741 #else
15742   DrawGameValue_Score(local_player->score);
15743 #endif
15744 }
15745
15746 void RaiseScoreElement(int element)
15747 {
15748   switch (element)
15749   {
15750     case EL_EMERALD:
15751     case EL_BD_DIAMOND:
15752     case EL_EMERALD_YELLOW:
15753     case EL_EMERALD_RED:
15754     case EL_EMERALD_PURPLE:
15755     case EL_SP_INFOTRON:
15756       RaiseScore(level.score[SC_EMERALD]);
15757       break;
15758     case EL_DIAMOND:
15759       RaiseScore(level.score[SC_DIAMOND]);
15760       break;
15761     case EL_CRYSTAL:
15762       RaiseScore(level.score[SC_CRYSTAL]);
15763       break;
15764     case EL_PEARL:
15765       RaiseScore(level.score[SC_PEARL]);
15766       break;
15767     case EL_BUG:
15768     case EL_BD_BUTTERFLY:
15769     case EL_SP_ELECTRON:
15770       RaiseScore(level.score[SC_BUG]);
15771       break;
15772     case EL_SPACESHIP:
15773     case EL_BD_FIREFLY:
15774     case EL_SP_SNIKSNAK:
15775       RaiseScore(level.score[SC_SPACESHIP]);
15776       break;
15777     case EL_YAMYAM:
15778     case EL_DARK_YAMYAM:
15779       RaiseScore(level.score[SC_YAMYAM]);
15780       break;
15781     case EL_ROBOT:
15782       RaiseScore(level.score[SC_ROBOT]);
15783       break;
15784     case EL_PACMAN:
15785       RaiseScore(level.score[SC_PACMAN]);
15786       break;
15787     case EL_NUT:
15788       RaiseScore(level.score[SC_NUT]);
15789       break;
15790     case EL_DYNAMITE:
15791     case EL_EM_DYNAMITE:
15792     case EL_SP_DISK_RED:
15793     case EL_DYNABOMB_INCREASE_NUMBER:
15794     case EL_DYNABOMB_INCREASE_SIZE:
15795     case EL_DYNABOMB_INCREASE_POWER:
15796       RaiseScore(level.score[SC_DYNAMITE]);
15797       break;
15798     case EL_SHIELD_NORMAL:
15799     case EL_SHIELD_DEADLY:
15800       RaiseScore(level.score[SC_SHIELD]);
15801       break;
15802     case EL_EXTRA_TIME:
15803       RaiseScore(level.extra_time_score);
15804       break;
15805     case EL_KEY_1:
15806     case EL_KEY_2:
15807     case EL_KEY_3:
15808     case EL_KEY_4:
15809     case EL_EM_KEY_1:
15810     case EL_EM_KEY_2:
15811     case EL_EM_KEY_3:
15812     case EL_EM_KEY_4:
15813     case EL_EMC_KEY_5:
15814     case EL_EMC_KEY_6:
15815     case EL_EMC_KEY_7:
15816     case EL_EMC_KEY_8:
15817     case EL_DC_KEY_WHITE:
15818       RaiseScore(level.score[SC_KEY]);
15819       break;
15820     default:
15821       RaiseScore(element_info[element].collect_score);
15822       break;
15823   }
15824 }
15825
15826 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15827 {
15828   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15829   {
15830 #if defined(NETWORK_AVALIABLE)
15831     if (options.network)
15832       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15833     else
15834 #endif
15835     {
15836       if (quick_quit)
15837       {
15838 #if 1
15839
15840 #if 1
15841         FadeSkipNextFadeIn();
15842 #else
15843         fading = fading_none;
15844 #endif
15845
15846 #else
15847         OpenDoor(DOOR_CLOSE_1);
15848 #endif
15849
15850         game_status = GAME_MODE_MAIN;
15851
15852 #if 1
15853         DrawAndFadeInMainMenu(REDRAW_FIELD);
15854 #else
15855         DrawMainMenu();
15856 #endif
15857       }
15858       else
15859       {
15860 #if 0
15861         FadeOut(REDRAW_FIELD);
15862 #endif
15863
15864         game_status = GAME_MODE_MAIN;
15865
15866         DrawAndFadeInMainMenu(REDRAW_FIELD);
15867       }
15868     }
15869   }
15870   else          /* continue playing the game */
15871   {
15872     if (tape.playing && tape.deactivate_display)
15873       TapeDeactivateDisplayOff(TRUE);
15874
15875     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15876
15877     if (tape.playing && tape.deactivate_display)
15878       TapeDeactivateDisplayOn();
15879   }
15880 }
15881
15882 void RequestQuitGame(boolean ask_if_really_quit)
15883 {
15884   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15885   boolean skip_request = AllPlayersGone || quick_quit;
15886
15887   RequestQuitGameExt(skip_request, quick_quit,
15888                      "Do you really want to quit the game ?");
15889 }
15890
15891
15892 /* ------------------------------------------------------------------------- */
15893 /* random generator functions                                                */
15894 /* ------------------------------------------------------------------------- */
15895
15896 unsigned int InitEngineRandom_RND(long seed)
15897 {
15898   game.num_random_calls = 0;
15899
15900 #if 0
15901   unsigned int rnd_seed = InitEngineRandom(seed);
15902
15903   printf("::: START RND: %d\n", rnd_seed);
15904
15905   return rnd_seed;
15906 #else
15907
15908   return InitEngineRandom(seed);
15909
15910 #endif
15911
15912 }
15913
15914 unsigned int RND(int max)
15915 {
15916   if (max > 0)
15917   {
15918     game.num_random_calls++;
15919
15920     return GetEngineRandom(max);
15921   }
15922
15923   return 0;
15924 }
15925
15926
15927 /* ------------------------------------------------------------------------- */
15928 /* game engine snapshot handling functions                                   */
15929 /* ------------------------------------------------------------------------- */
15930
15931 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15932
15933 struct EngineSnapshotInfo
15934 {
15935   /* runtime values for custom element collect score */
15936   int collect_score[NUM_CUSTOM_ELEMENTS];
15937
15938   /* runtime values for group element choice position */
15939   int choice_pos[NUM_GROUP_ELEMENTS];
15940
15941   /* runtime values for belt position animations */
15942   int belt_graphic[4 * NUM_BELT_PARTS];
15943   int belt_anim_mode[4 * NUM_BELT_PARTS];
15944 };
15945
15946 struct EngineSnapshotNodeInfo
15947 {
15948   void *buffer_orig;
15949   void *buffer_copy;
15950   int size;
15951 };
15952
15953 static struct EngineSnapshotInfo engine_snapshot_rnd;
15954 static ListNode *engine_snapshot_list = NULL;
15955 static char *snapshot_level_identifier = NULL;
15956 static int snapshot_level_nr = -1;
15957
15958 void FreeEngineSnapshot()
15959 {
15960   while (engine_snapshot_list != NULL)
15961     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15962                        checked_free);
15963
15964   setString(&snapshot_level_identifier, NULL);
15965   snapshot_level_nr = -1;
15966 }
15967
15968 static void SaveEngineSnapshotValues_RND()
15969 {
15970   static int belt_base_active_element[4] =
15971   {
15972     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15973     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15974     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15975     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15976   };
15977   int i, j;
15978
15979   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15980   {
15981     int element = EL_CUSTOM_START + i;
15982
15983     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15984   }
15985
15986   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15987   {
15988     int element = EL_GROUP_START + i;
15989
15990     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15991   }
15992
15993   for (i = 0; i < 4; i++)
15994   {
15995     for (j = 0; j < NUM_BELT_PARTS; j++)
15996     {
15997       int element = belt_base_active_element[i] + j;
15998       int graphic = el2img(element);
15999       int anim_mode = graphic_info[graphic].anim_mode;
16000
16001       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16002       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16003     }
16004   }
16005 }
16006
16007 static void LoadEngineSnapshotValues_RND()
16008 {
16009   unsigned long num_random_calls = game.num_random_calls;
16010   int i, j;
16011
16012   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16013   {
16014     int element = EL_CUSTOM_START + i;
16015
16016     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16017   }
16018
16019   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16020   {
16021     int element = EL_GROUP_START + i;
16022
16023     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16024   }
16025
16026   for (i = 0; i < 4; i++)
16027   {
16028     for (j = 0; j < NUM_BELT_PARTS; j++)
16029     {
16030       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16031       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16032
16033       graphic_info[graphic].anim_mode = anim_mode;
16034     }
16035   }
16036
16037   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16038   {
16039     InitRND(tape.random_seed);
16040     for (i = 0; i < num_random_calls; i++)
16041       RND(1);
16042   }
16043
16044   if (game.num_random_calls != num_random_calls)
16045   {
16046     Error(ERR_INFO, "number of random calls out of sync");
16047     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16048     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16049     Error(ERR_EXIT, "this should not happen -- please debug");
16050   }
16051 }
16052
16053 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16054 {
16055   struct EngineSnapshotNodeInfo *bi =
16056     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16057
16058   bi->buffer_orig = buffer;
16059   bi->buffer_copy = checked_malloc(size);
16060   bi->size = size;
16061
16062   memcpy(bi->buffer_copy, buffer, size);
16063
16064   addNodeToList(&engine_snapshot_list, NULL, bi);
16065 }
16066
16067 void SaveEngineSnapshot()
16068 {
16069   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16070
16071   if (level_editor_test_game)   /* do not save snapshots from editor */
16072     return;
16073
16074   /* copy some special values to a structure better suited for the snapshot */
16075
16076   SaveEngineSnapshotValues_RND();
16077   SaveEngineSnapshotValues_EM();
16078
16079   /* save values stored in special snapshot structure */
16080
16081   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16082   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16083
16084   /* save further RND engine values */
16085
16086   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16087   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16088   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16089
16090   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16091   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16092   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16093   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16094
16095   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16096   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16097   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16098   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16099   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16100
16101   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16102   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16103   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16104
16105   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16106
16107   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16108
16109   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16110   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16111
16112   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16113   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16114   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16115   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16116   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16117   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16118   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16119   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16120   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16121   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16122   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16123   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16124   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16125   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16126   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16127   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16128   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16129   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16130
16131   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16132   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16133
16134   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16135   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16136   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16137
16138   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16139   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16140
16141   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16142   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16143   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16144   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16145   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16146
16147   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16148   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16149
16150   /* save level identification information */
16151
16152   setString(&snapshot_level_identifier, leveldir_current->identifier);
16153   snapshot_level_nr = level_nr;
16154
16155 #if 0
16156   ListNode *node = engine_snapshot_list;
16157   int num_bytes = 0;
16158
16159   while (node != NULL)
16160   {
16161     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16162
16163     node = node->next;
16164   }
16165
16166   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16167 #endif
16168 }
16169
16170 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16171 {
16172   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16173 }
16174
16175 void LoadEngineSnapshot()
16176 {
16177   ListNode *node = engine_snapshot_list;
16178
16179   if (engine_snapshot_list == NULL)
16180     return;
16181
16182   while (node != NULL)
16183   {
16184     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16185
16186     node = node->next;
16187   }
16188
16189   /* restore special values from snapshot structure */
16190
16191   LoadEngineSnapshotValues_RND();
16192   LoadEngineSnapshotValues_EM();
16193 }
16194
16195 boolean CheckEngineSnapshot()
16196 {
16197   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16198           snapshot_level_nr == level_nr);
16199 }
16200
16201
16202 /* ---------- new game button stuff ---------------------------------------- */
16203
16204 /* graphic position values for game buttons */
16205 #define GAME_BUTTON_XSIZE       30
16206 #define GAME_BUTTON_YSIZE       30
16207 #define GAME_BUTTON_XPOS        5
16208 #define GAME_BUTTON_YPOS        215
16209 #define SOUND_BUTTON_XPOS       5
16210 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16211
16212 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16213 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16214 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16215 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16216 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16217 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16218
16219 static struct
16220 {
16221   int *x, *y;
16222   int gd_x, gd_y;
16223   int gadget_id;
16224   char *infotext;
16225 } gamebutton_info[NUM_GAME_BUTTONS] =
16226 {
16227 #if 1
16228   {
16229     &game.button.stop.x,        &game.button.stop.y,
16230     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16231     GAME_CTRL_ID_STOP,
16232     "stop game"
16233   },
16234   {
16235     &game.button.pause.x,       &game.button.pause.y,
16236     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16237     GAME_CTRL_ID_PAUSE,
16238     "pause game"
16239   },
16240   {
16241     &game.button.play.x,        &game.button.play.y,
16242     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16243     GAME_CTRL_ID_PLAY,
16244     "play game"
16245   },
16246   {
16247     &game.button.sound_music.x, &game.button.sound_music.y,
16248     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16249     SOUND_CTRL_ID_MUSIC,
16250     "background music on/off"
16251   },
16252   {
16253     &game.button.sound_loops.x, &game.button.sound_loops.y,
16254     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16255     SOUND_CTRL_ID_LOOPS,
16256     "sound loops on/off"
16257   },
16258   {
16259     &game.button.sound_simple.x,&game.button.sound_simple.y,
16260     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16261     SOUND_CTRL_ID_SIMPLE,
16262     "normal sounds on/off"
16263   }
16264 #else
16265   {
16266     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16267     GAME_CTRL_ID_STOP,
16268     "stop game"
16269   },
16270   {
16271     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16272     GAME_CTRL_ID_PAUSE,
16273     "pause game"
16274   },
16275   {
16276     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16277     GAME_CTRL_ID_PLAY,
16278     "play game"
16279   },
16280   {
16281     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16282     SOUND_CTRL_ID_MUSIC,
16283     "background music on/off"
16284   },
16285   {
16286     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16287     SOUND_CTRL_ID_LOOPS,
16288     "sound loops on/off"
16289   },
16290   {
16291     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16292     SOUND_CTRL_ID_SIMPLE,
16293     "normal sounds on/off"
16294   }
16295 #endif
16296 };
16297
16298 void CreateGameButtons()
16299 {
16300   int i;
16301
16302   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16303   {
16304     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16305     struct GadgetInfo *gi;
16306     int button_type;
16307     boolean checked;
16308     unsigned long event_mask;
16309     int x, y;
16310     int gd_xoffset, gd_yoffset;
16311     int gd_x1, gd_x2, gd_y1, gd_y2;
16312     int id = i;
16313
16314     x = DX + *gamebutton_info[i].x;
16315     y = DY + *gamebutton_info[i].y;
16316     gd_xoffset = gamebutton_info[i].gd_x;
16317     gd_yoffset = gamebutton_info[i].gd_y;
16318     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16319     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16320
16321     if (id == GAME_CTRL_ID_STOP ||
16322         id == GAME_CTRL_ID_PAUSE ||
16323         id == GAME_CTRL_ID_PLAY)
16324     {
16325       button_type = GD_TYPE_NORMAL_BUTTON;
16326       checked = FALSE;
16327       event_mask = GD_EVENT_RELEASED;
16328       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16329       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16330     }
16331     else
16332     {
16333       button_type = GD_TYPE_CHECK_BUTTON;
16334       checked =
16335         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16336          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16337          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16338       event_mask = GD_EVENT_PRESSED;
16339       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16340       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16341     }
16342
16343     gi = CreateGadget(GDI_CUSTOM_ID, id,
16344                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16345 #if 1
16346                       GDI_X, x,
16347                       GDI_Y, y,
16348 #else
16349                       GDI_X, DX + gd_xoffset,
16350                       GDI_Y, DY + gd_yoffset,
16351 #endif
16352                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16353                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16354                       GDI_TYPE, button_type,
16355                       GDI_STATE, GD_BUTTON_UNPRESSED,
16356                       GDI_CHECKED, checked,
16357                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16358                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16359                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16360                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16361                       GDI_DIRECT_DRAW, FALSE,
16362                       GDI_EVENT_MASK, event_mask,
16363                       GDI_CALLBACK_ACTION, HandleGameButtons,
16364                       GDI_END);
16365
16366     if (gi == NULL)
16367       Error(ERR_EXIT, "cannot create gadget");
16368
16369     game_gadget[id] = gi;
16370   }
16371 }
16372
16373 void FreeGameButtons()
16374 {
16375   int i;
16376
16377   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16378     FreeGadget(game_gadget[i]);
16379 }
16380
16381 static void MapGameButtons()
16382 {
16383   int i;
16384
16385   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16386     MapGadget(game_gadget[i]);
16387 }
16388
16389 void UnmapGameButtons()
16390 {
16391   int i;
16392
16393   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16394     UnmapGadget(game_gadget[i]);
16395 }
16396
16397 void RedrawGameButtons()
16398 {
16399   int i;
16400
16401   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16402     RedrawGadget(game_gadget[i]);
16403 }
16404
16405 static void HandleGameButtons(struct GadgetInfo *gi)
16406 {
16407   int id = gi->custom_id;
16408
16409   if (game_status != GAME_MODE_PLAYING)
16410     return;
16411
16412   switch (id)
16413   {
16414     case GAME_CTRL_ID_STOP:
16415       if (tape.playing)
16416         TapeStop();
16417       else
16418         RequestQuitGame(TRUE);
16419       break;
16420
16421     case GAME_CTRL_ID_PAUSE:
16422       if (options.network)
16423       {
16424 #if defined(NETWORK_AVALIABLE)
16425         if (tape.pausing)
16426           SendToServer_ContinuePlaying();
16427         else
16428           SendToServer_PausePlaying();
16429 #endif
16430       }
16431       else
16432         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16433       break;
16434
16435     case GAME_CTRL_ID_PLAY:
16436       if (tape.pausing)
16437       {
16438 #if defined(NETWORK_AVALIABLE)
16439         if (options.network)
16440           SendToServer_ContinuePlaying();
16441         else
16442 #endif
16443         {
16444           tape.pausing = FALSE;
16445           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16446         }
16447       }
16448       break;
16449
16450     case SOUND_CTRL_ID_MUSIC:
16451       if (setup.sound_music)
16452       { 
16453         setup.sound_music = FALSE;
16454         FadeMusic();
16455       }
16456       else if (audio.music_available)
16457       { 
16458         setup.sound = setup.sound_music = TRUE;
16459
16460         SetAudioMode(setup.sound);
16461
16462         PlayLevelMusic();
16463       }
16464       break;
16465
16466     case SOUND_CTRL_ID_LOOPS:
16467       if (setup.sound_loops)
16468         setup.sound_loops = FALSE;
16469       else if (audio.loops_available)
16470       {
16471         setup.sound = setup.sound_loops = TRUE;
16472         SetAudioMode(setup.sound);
16473       }
16474       break;
16475
16476     case SOUND_CTRL_ID_SIMPLE:
16477       if (setup.sound_simple)
16478         setup.sound_simple = FALSE;
16479       else if (audio.sound_available)
16480       {
16481         setup.sound = setup.sound_simple = TRUE;
16482         SetAudioMode(setup.sound);
16483       }
16484       break;
16485
16486     default:
16487       break;
16488   }
16489 }